用于定义建立实例及引用、判断实例的关键字
new
实例化对象。
1、构造对象的语法:
类名称 变量名称 = new 类名称(构造方法的调用(实参列表)); // 构造对象并保存
new 类名称(实参列表); // 匿名构造对象,不会有任何栈空间的指向,只可使用一次,随后就成为垃圾
举个 🌰:
代码说明:
-
一个包中只能有一个 public 的类或一个 public 的接口。
-
Person p = new Person(‘Doris’, 2, 3);
-
new:计算对象需要的内存大小,开辟空间,初始化为 0x0。
相当于 C 中的:p = malloc(sizeof(Person)); memset(p, 0x0, sizeof(Person));
-
Person(‘Doris’, 2, 3):调用构造方法,对属性初始化。
-
Person p:构造方法调用结束,返回一个指向新对象的引用。
Person p = 新的对象;
-
引用传递的本质:一块堆内存可以被多个栈内存所指向。
Person p1 = new Person();
Person p2 = new Person();
p1 = p2;
-
-
对象中存储的最主要的就是属性,也可以找到方法区对象相对应的类。
-
内存存储情况:
-
public Person(String n, int g, int a) {}
其中:n 是引用类型(所有引用类型都指向对象),g 和 a 是基本数据类型。
2、访问实例变量和实例方法:
对象(引用数据类型)必须在实例化后调用,否则会产生 NullPointerException(运行时错误),但编译时不会出错。
- 访问对象的属性
- 在类的内部:在同一类中访问,直接用“属性名称”访问。
- 在类的外部:在不同类中访问,使用“引用.属性名称”访问。
- 调用对象的方法
- 在类的内部:在同一类中调用,直接用“方法名称”调用。
- 在类的外部:在不同类中调用,使用“引用.方法名称(实参列表)”调用。
容易混淆的地方:
- 嵌套调用:
System.out.println(Arrays.toString(rotate(new int[] {1, 2, 3, 4, 5}, 3)));
实际执行过程:
int[] tmp1 = new int[] {1, 2, 3, 4, 5};
int[] tmp2 = rotate(tmp1, 3);
String tmp3 = Arrays.toString(tmp2);
System.out.println(tmp3);
- 链式调用:
class A {
public A create() {
return new A(); // 实际执行:A tmp = new A(); return tmp;
}
}
class B {
public static void main(String[] args) {
new A().create().create().create().create();
/**
* 实际执行:
* A tmp1 = new A();
* A tmp2 = tmp1.create();
* A tmp3 = tmp2.create();
* A tmp4 = tmp3.create();
* A tmp5 = tmp4.create();
}
}
-
Person p = null;
定义了一个没有指向任何对象的 Person 类型的引用(类类型的引用)。
-
Person p = new Person(…);
- 定义了一个 Person 类型的对象;
- 定义了一个指向 Person 类型对象的 Person 类型的引用(类类型的引用)。
引用类型和指向的对象类型是两回事。
3、对象的生命周期:
生命周期是运行期间的概念。对象的四个时期分为:
-
new 关键字:
- 从方法区中找相应的类,如果找不到就进行类的加载;
- 计算对象的大小;
- 在堆中合适的位置申请内存空间;
- memset(0x0):将整个内存空间初始化为 0 的变形。如果没有给值,取属性的默认值。
-
三个初始化:
- int a = 10;
- { a = 10; }
- 构造方法
根据定义的顺序优先执行前两个,然后遵循先父类后子类的顺序执行第三个。
-
对象真正的生命:正确使用的时期。
-
死去但保留尸体的阶段:即没有被 GC(Garbage Collection,垃圾收集)回收的阶段。
- GC 回收的是什么(什么是垃圾):以对象为单位,没有使用价值、没有引用指向的对象。
- 怎么判断什么是垃圾:由算法判断。
- GC 是否足够快速回收:根据算法进行快速回收(分类)。
有关 GC 的详细知识会在后续详述。
this
作用:普通方法调用和静态方法调用的调用栈。
1、调用构造方法
在构造方法中调用其他的构造方法,调用语句必须出现在第一行。
class Person() {
String name;
public Person(String n) {
name = n;
}
public Person() {
this("陌生人");
// name = "陌生人";
}
}
2、指向当前对象
- 通过 this 访问属性或方法,一般出现在发生了命名遮挡(name shadowing)的时候。
class Person() {
String name;
public Person(String name) {
this.name = name;
toString(); // this.toString();
}
public String toString() {...}
}
- this 代表的就是当前对象(current object)的引用。
class Person {
String name;
public Person(String name) { this.name = name; }
public String toString() { return this.name; }
public boolean equals(Person p) { return p.name = this.name; }
public void test() {
this.equals(this); // 只要对象调用了本类中的方法,this 就表示当前执行的对象
}
}
Person p = new Person(); // 则 this.equals(this); 实际表示为 p.equals(p);
普通属性和普通方法其实都绑定着一个隐含的对象,即 this。
举个 🌰:
class Solution {
Node reverseList(Node head) { ... }
}
class Main {
private static void testReverseList() {
new Solution().reverseList(head);
}
public static void main(String[] args) {
testReverseList();
}
}
super
1、作用:
- 明确访问父类的属性或者方法。
- 显式调用父类的构造方法,必须出现在第一行。
不是代表父类对象,可以抽象的理解为:执行属性中属于父类的那部分,明确调用父类的方法。
2、🌰:
class A {
public int a = 10;
public void print() {
System.out.println("父类的 print");
}
}
class B extends A {
public int a = 100;
public void print() {
System.out.println("子类的 print");
}
public void test() {
super a; // 父类的 a
super.print(); // 父类的 print
}
}
-
Java 中类的继承只允许单继承。
-
父类和子类之间的关系,涉及到 super 关键字:
-
类加载的关系:
- 子类的加载必须触发父类的加载,即父类的加载一定是发生在子类的加载之前的。
- 执行 main 方法就要进行类的加载,执行静态代码块。
-
对象实例化、构造的关系:
-
子类对象的构造过程会触发父类中属性的初始化过程,即父类属性的初始化一定发生在子类属性的初始化之前。
-
先执行构造到吗快,在执行构造方法,与书写位置无关。
属性初始化:定义时初始化 + 构造代码块(前两个按照书写顺序执行)+ 构造方法
和 super 调用父类构造方法的位置无关。
-
-
instanceof
1、语法:
引用 instanceof 类型(类类型、接口类型)
list instanceof List
作用:list 引用指向的对象是否能被 List 类型的引用指向。
主要用于向下转型时判断对象是否为类型引用的指向。
2、向下转型:
-
解决调用方法由引用决定,所以为了使用只有子类型独有的方法,需要向下转型。
-
向下转型是不自然的,需要强制转换。
A ab = new B();
ab.methodOfB(); // Compile Error
B bb = (B)ab;
// 如果 ab 实际上不是指向 B 类型的对象,不会有编译错误
// 但会触发运行时错误,会抛出异常 ClassCastException
举个 🌰:
List a = new ArrayList();
// 如果需要调用的只有 ArrayList 支持的方法
ArrayList b = (ArrayList) a; // 需要强制转换
// 需要保证 a 指向的对象可以被 ArrayList 引用指向(是 ArrayList 的子类类型的对象)
转换的正确性需要开发者自行保证。
为了安全,由 instanceof 运算符做运行时检查。
ab instanceof B; // ab 引用指向的对象能否被 B 类型的引用指向
3、向上转型:
- 把引用类型转换为另一种引用类型(类型层级属于其上层的)。
- 向上转型是自然的,不需要强制转换。