【java】— 类和对象(这一篇就够了)
郑州吴彦祖772 2024-10-23 11:35:16 阅读 71
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦
首先在讲解这个之前,我们需要了解什么是对象呢?
面向对象是什么意思呢?接下来让我为大家讲解吧!
1:认识面向对象
1.1:面向过程和面向对象有什么区别?
首先我们拿传统的洗衣机为例子:(面向过程)
传统的方式:
注重的是洗衣服的过程
,少了一个环节可能都不行。
而且不同衣服洗的方式,时间长度,拧干方式都不同,处理起来就比较麻烦。
如果将来要洗鞋子,那就是另一种放方式。
按照该种方式来写代码,将来
扩展或者维护起来会比较麻烦
因为我们每次都要更改里面的细节,就会很麻烦
但是现代的洗衣服的方法是这个样子:(面向对象)
面向对象和面向过程的区别:
面向对象只关心人和对象的交互,不用关心里面的太多细节(就像人根本不用担心洗衣机到底是怎么洗衣服的)
所以用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好
1.2:什么是面向对象?
Java
是一门纯面向对象的语言
(Object Oriented Program
,简称
OOP)
,在面向对象的世界里,一切皆为对象。
面
向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情
。(直接面向对象而不用管里面的细节,就可以让我们省去很多麻烦)用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好
注意:
面向过程和面相对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
解释完面向对象就不得不说类了
2. 类的定义
面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如:洗衣机。
但是洗衣机计算机并不认识,需要开发人员告诉给计算机什么是洗衣机。
就比如:这个洗衣机的各种参数我们该怎么定义呢,洗衣机这个类里面洗衣服的方法我们该怎么定义呢?有了这个类,下次我们就可以直接使用了!
2.1:简单认识一下类
类是用来对一个实体
(
)
来进行描述的
,主要描述该实体
(
对象
)
具有哪些属性
(
外观尺寸等
)
,哪些功能
(
用来干啥)
,描述完成后计算机就可以识别了
比如:洗衣机,它是一个品牌,在
Java
中可以将其看成是一个类别。
属性:产品品牌,型号,产品重量,外观尺寸,颜色
...
功能:洗衣,烘干、定时
....
然后我们就可以在java里面进行定义这个类了
2.2:类的定义
首先定义类的注意事项:
1.
一般一个文件当中只定义一个类(可以有好几个,但是被public修饰的类只能有一个)
2. main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
注意3,注意4 下面我会讲解
3. public
修饰的类必须要和文件名相同
4.
不要轻易去修改
public
修饰的类的名称,如果要修改,通过开发工具修改
java里面定义类要使用一个关键字:class
并且当我们新建一个类时,Test.java文件里面就会多出一个类
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。
成员方法主要说明类 具有哪些功能,称为类的成员方法。(可不能叫类方法)
类方法后面我会讲解
采用
Java
语言将洗衣机类在计算机中定义完成,经过
javac
编译之后形成
.class
文件,在
JVM
的基础上计算机就可以 识别了。
上述注意3:不要轻易去修改public修饰的类的名称,如果要修改,通过开发工具修改
但这种情况只适合一个java文件里面只有一个类的情况:
譬如这个Test2文件里面有两个类:Test2,Dog前者被public修饰,然后我修改这个Test2变成Test3就不行了
上述注意4:main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
main方法是程序的入口,每个类里面都可以有main方法但是:
虽然Java允许在多个类中定义main方法,但每个程序在运行时通常只有一个入口点(即一个main方法)(就是尽管你可以在多个类中定义main方法,但在运行程序时,你只会选择其中一个作为启动点)。多个main方法的存在主要是为了代码的模块化和可能的重用,而不是表示有多个独立的程序。
我们每次鼠标在哪个类里面右击运行,对应的就是哪个类里面的main方法
既然我们说有了类才有对象,现在我们学会了类的定义,那么对象到底怎么产生呢?
3.类的实例化:
3.1:如何进行实例化
我们定义了一个类,就相当于在计算机中定义了一种新的类型
,与
int
,
double
类似,只不过
int
和
double
是
java
语言自带的内置类型,而类是用户自定义了一个新的类型,比如上述的:Dog
类。
它们都是类
(
一种新定义的类型)
有了这些自定义的类型之后,就可以使用这些类来定义实例
(
或者称为对象
)
。
用类类型创建对象的过程,称为类的实例化
,在
java
中采用
new
关键字,配合类名来实例化对象
代码以及结果:
3.2类的注意事项:
注意:
1. new
关键字用于创建一个对象的实例
.
2. 使用
.
来访问对象中的属性和方法
.
3. 同一个类可以创建多个实例.实例化出的对象 占用实际的物理空间,存储类成员变量
4..
类是一种自定义的类型
,可以用来定义变量
.
5.(类就是一个模型一样的东西)用来对一个实体进行描述,限定了类有哪些成员
.
做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图
,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
4. this引用:
4.1.this引用关于成员变量和局部变量的问题
this
引用指向当前对象
(
成员方法运行时调用该成员方法的对象
)
,在成员方法中所有成员变量的操作,都是通过该
引用去访问
。
譬如这种运行就很正常
<code>class Date{
public int year;
public int month;
public int day;
public void setDate(int y,int m,int d){
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println("年:"+year+"月:"+month+"日:"+day);
}
}
public class Test2 {
public static void main(String[] args) {
Date date = new Date();
date.setDate(2024,10,3);
date.printDate();
}
但是如果形参和成员变量重名了呢?
结果:其实这就相当于自己给自己赋值:(局部变量优先)
这个我们就要聊一下局部变量和成员变量的作用域了
成员变量定义在类的在 Java 中,成员变量和局部变量可以有相同的名字,但它们的作用域不同,因此不会直接冲突。成员变量属于类的实例,而局部变量属于方法或代码块。
成员变量:
定义在类的主体中,但在任何方法或构造函数之外。它们与类的每个实例关联,因此每个对象都有自己的成员变量副本。
局部变量:
定义在方法、构造函数或任何代码块内部。它们只在定义它们的代码块内可见
作用域和可见性:
成员变量:在整个类中可见,除非被更内层的局部变量遮蔽。局部变量:只在定义它的代码块内可见。
Java 编译器允许这种命名方式,因为局部变量和成员变量在作用域上是隔离的。局部变量只在方法或代码块中有效,而成员变量在整个类中有效。这种设计允许在方法内部临时覆盖成员变量的值,而不影响类的其他部分。
接下来我用一张图表示
如果我们不想照成上述那个结果我们完全可以用下面的这个方法:this引用
这样完全解决了局部变量优先的问题,因为用this引用就表明了这个year变量是成员变量
但是this不能引用静态成员变量(不属于对象)(这个其实叫做类变量,我上述和说的类方法同理是 静态成员方法)
4.2. this引用的特点:
最后关于this的一些深度一点的东西:
1. this
的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
date1引用就调用这个引用指向的对象
2. this
只能在
"
成员方法
"
中使用
3. this
是
“
成员方法
”
第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法 对象的引用传递给该成员方法,this
负责来接收
最后在类里面,调用成员变量的时候最好加上this引用
5:成员变量的初始化以及构造方法:
5.1. 默认初始化:
局部变量必须要初始化才能使用,为什么字段声明之后没有给值依然可以使用?
其实都要初始化,但是成员变量也就是字段会进行默认初始化
各个数据类型的默认数值
数据库类型 | 默认值 |
byte | 0 |
char |
'\u0000'
|
short
| 0
|
int | 0 |
long | 0L |
float | 0f
|
double | 0 |
boolean | false |
reference | null |
接下来一个代码带大家看一下:
<code>class Student{
public String name;//这些成员变量我都没有进行初始化
public int age; //都是默认初始化
public void eat(){
System.out.println(name+"玩游戏"+age);
}
}
public class Test1 {
public static void main(String[] args) {
Student student = new Student();
student.eat();
}
}
在程序层面只是简单的一条语句,在
JVM
层面需要做好多事情,下面简单介绍下:
1.
检测对象对应的类是否加载了,如果没有加载则加载
2.
为对象分配内存空间
3.
处理并发安全问题
比如:多个线程同时申请对象(同时实例化多个对象),
JVM
要保证给对象分配的空间不冲突
4.
初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
5.
设置对象头信息
(
关于对象内存模型后面会介绍
)
6.
调用构造方法,给对象中各个成员赋值
分配内存空间和初始化对象(包括调用构造函数)是创建对象这一行为不可分割的两个部分。你不能说“先实例化对象,然后才给该对象分配空间”,因为这两个动作是紧密耦合的。
上述我们提到了构造方法,接下来我们讲解一下:
5.2.构造方法:
概念:
5.2.1.
名字必须与类名相同
5.2.2.
没有返回值类型,设置为
void
也不行
5.2.3.
创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
(
相当于人的出生,每个人只能出生一次
)
5.2.4.
构造方法可以重载
(
用户根据自己的需求提供不同参数的构造方法
)
5.2.5..
绝大多数情况下使用
public
来修饰,特殊场景下会被
private
修饰
(
后序讲单例模式时会遇到
)
但是我们上述很多实例化的对象都没有构造方法呀,也行?
其实是不是没有,只是编译器自动给我们生成了一个不带参数的构造方法;
这是一个我们自己加上去的构造方法(不带参数)(类名:Student)
带参数的构造方法:
<code>
class Student{
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
System.out.println("带有两个参数的构造方法");
}
public Student(){
System.out.println("带有一个参数的构造方法");
}
public void eat(){
System.out.println(name+"玩游戏"+age);
}
}
public class Test1 {
public static void main(String[] args) {
Student student1 = new Student("lihua",19);//初始化的时候就给成员变量赋值了
Student student = new Student();
}
}
并且当你有了构造方法之后编译器就不会默认给你生成了(下面这种情况就会报错)
class Student{
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
System.out.println("带有两个参数的构造方法");
}
}
public class Test1 {
public static void main(String[] args) {
Student student = new Student();
}
}
4.构成方法的重载:
5.2.6通过this去调用其他的构造方法
这里this必须在构造方法里面的第一条语句,不然就错了
例子:
5.2.7:不能成环:
public
Date
(){
this
(
1900
,
1
,
1
);
}
public
Date
(
int
year
,
int
month
,
int
day
) {
this
();
}
你俩互相调用,死循环
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:
Error:(19, 12) java:
递归构造器调用
*/
5.3. 就地初始化:
<code> class Student1{
public String name = "萧炎";
public int age = 18;
public float score = 88.6f;
public Student1(String name,int age){
this.name = name;//尽管我已经就初始化了,但是还可以改变值
this.age = age;
System.out.println("带有两个参数的构造方法"+name);
}
}
public class Test3 {
public static void main(String[] args) {
Student1 student1 = new Student1("任风",19);
}
}
注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中(像这个虽然已经我已经就地初始化了,但是我通过构造方法又改变了他的值)(各个成员变量的值肯定是后来我改变之后的值了)
总结一共三个初始化:
默认初始化通过构造方法进行初始化就地初始化
最后你要知道
当你使用
new
关键字来创建一个对象时,Java虚拟机(JVM)会执行以下步骤:
分配内存空间:JVM首先会在堆(Heap)中分配足够的内存空间来存储新对象。
初始化对象:接下来,JVM会将分配的内存空间初始化为零(对于基本类型)或者
null
(对于对象引用)。
调用构造函数:然后,JVM会调用该对象的构造函数(constructor)来初始化对象的字段。这一步是对象真正成为“有效”对象的关键,因为构造函数中通常会设置一些初始状态或者执行一些必要的初始化逻辑。
返回对象引用:最后,
new
表达式会返回新创建对象的引用,这个引用可以被存储在变量中,用于后续操作。
最后才会成功实例化出来一个对象
这些都是实例化对象的过程
6. 封装:
封装是一种面向对象编程(OOP)的重要原则,其实就是通过private对类的成员(变量或者方法)进行隐藏,只是对类外提供公开的接口
意义:隐藏类的实现细节,从而达到安全性
封装概念:面向对象程序三大特性:封装、继承、多态
。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节
。
对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,
CPU
内部是如何设计的等,用户 只需要知道,怎么开机,怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳
子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可
。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
6.1:private
被private修饰了就是封装,被private了的方法在类外也不能调用了得到或者修改只能通过get和set方法,下面会讲:
被private修饰的成员变量,只能在这一个类里面使用:
这样就会报错;
但是我们还是想得到或者改变这个成员变量该怎么办呢?
右击鼠标:然后选中Generate,就会出现两个函数get和set函数,还有构造方法啥的
(一键生成代码)
6.2:访问限定符
Java
中主要通过类和访问权限来实现封装:
类可以将数据以及封装数据的方法结合在一起
,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用
。
Java
中提供了四种访问限定符:
private我们已经讲过,default在下面包的知识里面我会讲解
6.3.包:就是用来组装类的(就是文件夹)
包的概念:为了更好的管理类,把多个类收集在一起成为一组,称为软件包
在
Java
中也引入了包,
包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式
,比如:一 个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在
不同的包中即可
。
(就像不同的文件夹下面可以有相同的文件名称的文件)
1.譬如这些都是utli里面的类
2.另一种看类在哪的方法在外部库里面
3.看Date类里面的方法,按住ctrl点击Date,然后点击structure或者Alt+7
6.3.1:包的导入:
Java
中已经提供了很多现成的类供我们使用
.
例如
Date
类:可以使用
java.util.Date
导入
java.util
这个包中的
Date 类
我们可以使用import导入包
如果我们要使用util里面的其他类,我们就可以这样写
但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况
<code>import java.util.*
但是如果我们要的类在两个包里面都有
// util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
这个时候我们还是需要使用完整的类名:
6.3.2:静态导入:
可以使用
import static
导入包中静态的方法和字段
import static
java
.
lang
.
Math
.
*
;本来应该是
import
java
.
lang
.
Math
public class
Test
{
public static
void
main
(
String
[]
args
) {
double
x
=
30
;
double
y
=
40
;
//
静态导入的方式写起来更方便一些
.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double
result
=
sqrt
(
pow
(
x
,
2
)
+
pow
(
y
,
2
));
System
.
out
.
println
(
result
);
}
}
注意事项
:
import
和
C++
的
#include
差别很大
.
C++
必须
#include
来引入其他文件内容(并且全部导入文件里面的内容)
但是
Java
不需要
. import 只是为了写代码的时候更方便
.(用到谁就导入谁)
import
更类似于
C++
的
namespace
和
using
6.4:自定义包
规则:
在文件的最上方加上一个 package 语句指定该代码在哪个包中. 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ). 如果一个类没有 package 语句, 则该类被放到一个默认包中.(src包里面)包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
6.4.1 操作步骤:
1.
在
IDEA
中先新建一个包
:
右键
src ->
新建
->
包
2.
在弹出的对话框中输入包名
,
例如
com.baidu.www
3:这个时候我们要对IDEA进行一些设置,就会出现三个包,分别是com,baidu,www
选中上面的设置,然后这么选中,把这个对钩取消就好了
就会出现这个结果
4.如果要在baidu地下再新建一个包的话还是右键baudu然后package就好了
5.同时我们还看到了新建的TestDemo2.java文件上面显示在demo这个包里面
6.4.2:包访问权限
default的同一个包里面都可以访问,
但是在另外一个包里面就不行
6.5 常见的包:
1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。里面的类我们直接用就好了不用导入这个包了
2. java.lang.reflect:java 反射编程包;但是这个reflect也是一个包并且是java.lang里面的包(包中包),这个包中包里面的类也要我们自己去导入
3. java.net:进行网络编程开发包。 . java.sql:进行数据库开发的支持包。
4. java.util:是java提供的工具程序包。(集合类等) 非常重要
5. java.io:I/O编程开发包。
7.static成员
问题:三个同班同学来上课,他们的班级都一样,那我们要定义一个学生类,里面有班级这个成员变量,岂不是给他们每个人实例化一个学生类的时候都要再实例化班级这个成员变量(实例变量)?
这样肯定可以,但是会浪费空间毕竟
这个教室的属性并不需要每个学生对象中都存储一份,而是需要让所
有的学生来共享
这个时候我们引入static
在
Java
中,被
static
修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对
象,是所有对象所共享的
。
这个时候classRoom这个类成员就不属于任何对象了,它是属于类的,
连对象都不必实例化
7.1. static修饰成员变量:(类变量)
类变量直接通过类访问(Student.classRoom)(这个Student是类名)
static
修饰的成员变量,称为静态成员变量
,静态成员变量最大的特性:
不属于某个具体的对象,是所有对象所共
享的
。
【静态成员变量特性】
1.
不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2.
既可以通过对象访问(脱裤子放屁,多此一举),也可以通过类名访问,但一般更推荐使用类名访问
3.
类变量存储在方法区当中(成员方法和类方法也是)
4.
生命周期伴随类的一生
(
即:随类的加载而创建,随类的卸载而销毁
)
7.2.static修饰成员方法:(类方法)
访问和类变量一样(Student.func())
【
静态方法特性
】
1.
不属于某个具体的对象,是类方法(不依赖于对象)(但是其他方法依赖对象的话,你在这个静态方法里面就不能调用)(它依赖对象,你就给它初始化一个对象就可以了)
2.
可以通过对象调用(不推荐),也可以通过类名
.
静态方法名
(...)
方式调用,更推荐使用后者
3. 一般不能
在静态方法中访问任何非静态成员变量(this更不行,this这个引用就是引用对象的,通过对象的引用里的地址传递给this)(和下面静态方法里面的图一个道理)
4.
静态方法中不能调用任何非静态方法,因为非静态方法有
this
参数,在静态方法中调用时候无法传递
this
引用,除非实例化对象)
下一个图
5.
静态方法无法重写,不能用来实现多态
(
此处先暂时不用管,后序多态位置我会详细讲解
)
。
当然你在普通成员方法里面直接带直接调用静态的方法:包可以的!
7.3 static成员变量初始化
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化
。
1.
就地初始化
就地初始化指的是:在定义时直接给出初始值
2.
静态代码块初始化
那什么是代码块呢?继续往后看
:~~~
8.代码块:
使用
{}
定义的一段代码称为代码块
。根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块 构造块 静态块 同步代码块(后续讲解多线程部分我会再出博客给大家讲解
8.1. 普通代码块:
直接写呗,不过别忘了作用域
8.2. 构造代码块:
构造块:定义在类中的代码块
(
不加修饰符
)
。也叫:
实例代码块
。
构造代码块一般用于初始化实例成员变量
大家可以看看这块代码输出的是啥,以及顺序
<code>public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
class Student {
public String name;
public int age;
public static int classRoom = 111;
public Student(){
System.out.println("不带参数的构造方法");
}
{
System.out.println("实例代码块,用来初始化成员变量的");
this.name = "liHua";
this.age = 18;
}
public void show(){
System.out.println(this.name+"吃饭"+"年龄:"+age);
}
}
按理说应该是(其实没学之前我也不知道哈哈)
大家可以看到实例代码块是最先去执行的 优于构造方法(并且已经帮我们初始化好成员变量了)
8.3:静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
这次的代码多加了一个静态代码块在最后
<code>public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
class Student {
public String name;
public int age;
public static int classRoom = 111;
public Student(){
System.out.println("不带参数的构造方法");
}
{
System.out.println("实例代码块,用来初始化成员变量的");
this.name = "liHua";
this.age = 18;
}
static {
System.out.println("静态代码块");
Student.classRoom = 109;
}
public void show(){
System.out.println(this.name+"吃饭"+"年龄:"+age);
}
}
结果:可以看出静态代码块是第一个被执行的>示例代码块>构造方法
但是如果我又初始化一个对象呢?(我删去了show方法相比于上面的代码)
<code>public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println("================");
Student student1 = new Student();
}
}
结果可以看出:静态代码块不管生成多少个对象,其只会执行一次
最后注意:
静态代码块不管生成多少个对象,其只会执行一次 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的(加载类的时候就给它开辟空间并且进行初始化了)(只要类加载就会被执行)如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并) 实例代码块只有在创建对象时才会被执行
上述就是java里(类和对象以及封装)的全部内容了,能看到这里您一定对小编的文章有了一定的认可,
真的超级累,有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正
您的支持就是我最大的动力!!!!
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。