一:类和对象
Java是一门纯面向对象的语言,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好
1.1 什么是类和对象
那么什么是类,什么是对象呢?通俗一点理解就是,类就是一个模板,用来对一个实体进行描述,限定了对象有哪些属性和行为.我们按照这个模板来实例化出许多个对象,
在java中,我们描述一个对象是通过描述这个对象的属性和行为进行描述的,举个例子
当以汽车为例进行描述时,可以将汽车的属性和行为抽象为一个类的属性和方法。
属性(属性描述了汽车的特征):
- 品牌:描述汽车的品牌,如“奔驰”、“宝马”等。
- 颜色:描述汽车的外观颜色,如“红色”、“白色”等。
- 型号:描述汽车的型号,如“SUV”、“跑车”等。
行为(行为描述了汽车能够执行的动作):
- 加速:描述汽车加速的动作,如“踩油门加速”。
- 刹车:描述汽车刹车的动作,如“踩刹车减速停车”。
- 换挡:描述汽车换挡的动作,如“挂1挡、2挡等”。
通过定义汽车类,并在类中定义相应的属性和方法,就能够对汽车进行描述和操作。这样的描述方式能够更清楚地表示汽车对象的特征和能力,使我们可以更好地理解和模拟现实生活中的汽车行为。
因为类是一个模板,它是一个抽象的东西,但是由他产生的对象则是具体的,所以我们经常说实例化一个这个类的对象
1.2 类的定义格式
class ClassName{ field; // 属性 即 成员变量 method; // 行为 即 成员方法 }
class为定义类的关键字,ClassName为类的名字,{ }中为类的主体。
下面通过举例来说明类的定义
当我们描述一个狗狗的时候可以通过狗狗的名字和颜色,以及狗狗狗叫和摇尾巴的行为来进行描述
所以我们可以定义一个这样的狗狗类:
class PetDog { // 狗的属性 public String name;//名字 public String color;//颜色 // 狗的行为 public void barks() { System.out.println(name + ": 旺旺旺~~~"); } public void wag() { System.out.println(name + ": 摇尾巴~~~"); } }
- 属性主要是用来描述类的,属性称之为成员变量。
- 方法主要说明类具有哪些功能,称为类的成员方法。
1.3局部变量和成员变量
注意成员变量和局部变量是两个不同的概念
成员变量是定义在类中的变量,可以在类的任何方法中使用。它们的作用域是整个类,可以通过对象来访问。通常,成员变量用于存储对象的状态或属性,例如对象的名称、年龄等。成员变量在对象创建时被初始化,并且可以在整个对象的生命周期内被访问和修改。
局部变量是在方法、代码块或构造函数中定义的变量。它们的作用域限定在声明它们的方法、代码块或构造函数的范围内。局部变量只在其定义的范围内可见,并且只能在该范围内访问。局部变量在方法执行时被创建,并在方法结束后被销毁。它们通常用于存储临时数据和方法内部的计算结果。
- 成员变量可以不用进行初始化
- 局部变量则必修要进行初始化
以下是类使用过程中的两个注意点:
- main方法所在的类一般要使用public修饰
- public修饰的类必须要和文件名相同
在Java中,公有类是指被声明为public的类,公有类可以让其他类对这个公有类进行访问。
main方法是Java程序的入口点,是程序执行的起点。Java程序必须包含一个main方法。当程序运行时,Java虚拟机(JVM)会根据这个方法开始执行程序。
main一定要在公有类的主要原因是为了让Java虚拟机能够找到程序的入口,Java虚拟机需要通过反射来调用main方法,而反射只能访问公有方法。如果将main方法定义为非公有的,虚拟机将无法找到并调用该方法,导致程序无法执行。
此外,将main方法置于公有类中还有助于程序的可读性和维护性。公有类作为程序的入口点,可以更方便地理解整个程序的结构和功能。公有类也可以在其他程序中直接被引用,使程序更具可重用性和扩展性。
1.4类的实例化
我们已经有了类了,那么我们该如何通过类来实例化一个对象呢?
class PetDog { // 狗的属性 public String name;//名字 public String color;//颜色 // 狗的行为 public void barks() { System.out.println(name + ": 旺旺旺~~~"); } public void wag() { System.out.println(name + ": 摇尾巴~~~"); } }
我们可以通过
- PetDog dogh = new PetDog();
这行代码来完成对类的示例化,下面我来详细解释一下这段代码的意思:
new是java中的一个关键字,用来创建一个空间,而new PetDog就是创建一个PetDog类型的空间,并且new PetDog会返回创建好空间的地址,而new PetDog后的( )则是传给构造方法的参数
接着dogh是一个引用,我们通过赋值符号 = 将创建好空间的地址赋给了dogh,所以dogh指向了这个空间,PetDog dogh就表示dogh这个引用的类型是PetDog的为什么引用也有类型呢?主要是想说明你这个引用指向的空间应该是PetDog类型的或者子类空间类型的空间(还没有讲到子类,这里简单有个印象即可),并说明dogh能管理的空间只有PetDog类型这么大的空间
下面举一个简单的代码例子来演示一下:
class PetDog { // 狗的属性 public String name;//名字 public String color;//颜色 // 狗的行为 public void barks() { System.out.println(name + ": 旺旺旺~~~"); } public void wag() { System.out.println(name + ": 摇尾巴~~~"); } } public class Main{ public static void main(String[] args) { PetDog dogh = new PetDog(); //通过new实例化对象 dogh.name = "阿黄"; dogh.color = "黑黄"; dogh.barks(); dogh.wag(); } }
这段代码的输出结果是:
阿黄: 旺旺旺~~~
阿黄: 摇尾巴~~~
- 在java中我们使用 . 来访问对象中的属性和方法.
- 并且同一个类可以创建多个实例.
1.5 this引用
首先我们来思考一下为什么要有this引用,下面通过一个代码来引出:
public class Person { String name; int age; public void setPerson(String n, int a) { name = n; age = a; } public void printDetails() { System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); } public static void main(String[] args) { Person person = new Person(); person.setPerson(张三,20); person.printDetails(); } }
再这段代码中,我们先实例化了一个Person对象,接着通过person这个对象中的setPerson方法对name和age进行了实例化,接着通过person这个对象中的printDetails方法对信息进行打印,运行结果如下:
姓名:张三
年龄:20
但是万一我们不小心将setPerson方法错误的写成了
public void setPerson(String name, int age) { name = name; age = age; }
这样我们就分不清楚我们传入的name是哪个了,并且根据局部优先的原则,程序会将name = name 被解析成了,将成员变量name赋值给形参变量name,这和我们的本意相反,我们需要把我们传给形参的值赋值给成员变量。
那么我们该如何避免这种情况呢?答案是通过this
那么什么是this呢?
- this代表着当前对象的引用
this引用有三个很重要的作用:
- 通过this区分局部变量和成员变量
我们可以通过this.name来告诉编译器,这个name是成员变量中的name,而不是局部变量的name
- 在类中通过this.方法名()来调用本类的成员方法
我们可以在存在于同一个类的两个方法中实现方法之间的调用
- 通过this()来在构造方法中调用其他构造方法
我们可以通过this()来在构造方法中调用其他构造方法,并且这一行代码应该在第一行
所以我们可以对上述的代码进行改进
public class Person { String name; int age; public void setPerson(String name, int age) { this.name = name; this.age = age; } public void printDetails() { System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); } public static void main(String[] args) { Person person = new Person(); person.setPerson(张三,20); person.printDetails(); } }
this.name = name;表示将局部变量name赋值给成员变量name,这样就可以避免了冲突,而且方便我们更加直观的去理解代码
1.6 构造方法
public class Person { String name; int age; public void setPerson(String name, int age) { this.name = name; this.age = age; } public void printDetails() { System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); } public static void main(String[] args) { Person person = new Person(); person.setPerson(张三,20); person.printDetails(); } }
在这段代码中,我们每创建一个对象都要通过setPerson这个方法来对对象进行初始化,在java中,创建对象是一个很频繁的事情,所以我们没创建一个对象就要去调用一次setPerson方法是不是有点太麻烦了?
所以java给我们提供了一个方法,名为构造方法,构造方法每创建一次对象,就会为这个对象调用一次构造方法,所以我们就可以通过构造方法来完成对对象的初始化
所以上述代码我们可以改成:
public class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } public void printDetails() { System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); } public static void main(String[] args) { Person person = new Person(); person.setPerson(张三,20); person.printDetails(); } }
构造方法的特性:
- 构造方法的名字必须与类名相同
- 没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
下面我来对这些特性进行说明:
构造方法的名字为什么要和类名相同?
- 方便代码阅读和理解,如果名字不一致的话,会让人感到困惑,而如果名字一致的话,就很容易让人明白这是构造方法,用来帮助对象的初始化
- 方便编译器确定该类的构造方法是哪一个,避免了由构造方法名称不明确带来的编译错误
为什么构造方法没有返回值类型,void也不可以?
- 因为构造方法的主要目的就是为了帮助对象完成初始化,我们并不需要返回值,并且通过不写返回值,能够更好的告诉我们这个方法是构造方法,用于帮助对象的初始化
注意:当一个类实例化了一个对象,那么就一定要调用一次构造方法,如果用户没有定义构造方法,那么系统会默认生成一份没有参数的构造方法供我们使用,但是如果我们写了任意一个构造方法,那么系统就不会帮我们生成这个无参的构造方法了
构造方法在实例化对象的时候一定要被调用,如果没有调用则会报错
PetDog dogh = new PetDog(张三,20);我们可以在实例化对象的时候通过()来将张三和20这两个参数传递给构造方法,记得顺序和类型要对应上哦
在构造方法中,我们可以通过this()来调用其他的构造方法,其中this中的括号要放入对应的参数比如:this (1900,1,1);且this (1900,1,1);必须是第一条语句
二:成员变量的默认初始化
为什么局部变量在使用的时候必须要初始化,而成员变量可以不用呢?要搞清楚这个过程,就需要知道 new 关键字背后所发生的一些事情:在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
- 检测对象对应的类是否加载了,如果没有加载则加载
- 为对象分配内存空间
- 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突 - 初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如: - 设置对象头信息(关于对象内存模型后面会介绍)
- 调用构造方法,给对象中各个成员赋值
所以当我们实例化一个对象之后,就已经给成员变量赋值了,那些值都是0的变式,当然我们也可以在类中对成员变量进行赋值比如:
public class Date { public int year = 1900; public int month = 1; public int day = 1; }