零、关于继承的补充
❓ 子类对象的内存中是否包含父类中定义的private
成员变量 ?
📋 有。只是不能够直接访问而已,可让父类提供public
的成员方法以访问private
的成员变量。
一、final
✏️ 被final
修饰的类不能被继承
✏️ 被final
修饰的方法不能被重写
✏️ 被final
修饰的变量只能进行1次赋值
📋final
可修饰局部变量
📋final
可修饰成员变量
✏️ Java 中的常量(Constant)指 :同时被static
和final
修饰的成员变量
📋 若将 基本类型或 字符串定义为常量(static final
),并且 在编译时就能确定值:编译器会使用 常量值替换各处的 常量名(类似 C 语言中的 宏替换)
📋 这种常量被称作 编译时常量(compile-time constant)
二、嵌套类(Nested Class)
✏️ 嵌套类:定义在另一类中的类
public class OuterClass {
/* 静态嵌套类 */
static class StaticNestedClass {
}
/* 非静态嵌套类(内部类) */
class InnerClass {
}
}
✏️ 没有被static
修饰的嵌套类叫做内部类
✏️ 嵌套类外层的类叫做外部类(Outer Class)
✏️ 最外层的外部类叫做顶级类(Top-level Class)
三、内部类(Inner Class)
✏️ 内部类:没有被static
修饰的嵌套类(也叫做非静态嵌套类)
✏️ 与实例变量、实例方法一样,内部类与外部类的实例挂钩
✒️ 必须先创建外部类实例,然后通过 外部类实例创建内部类实例
✏️ 内部类不能定义除编译时常量之外的任何static
成员
✏️ 内部类可以直接访问外部类中的所有成员(即使是private
成员)
✏️ 外部类可以直接访问内部类实例的成员变量和方法(即使是private
)
先创建外部类实例,然后通过外部类实例创建内部类实例:
public class Person {
/* Person 类的一个内部类(没有被 static 修饰) */
public class Hand {
}
}
class TestDemo {
public static void main(String[] args) {
/* 先创建外部类实例 */
Person p = new Person();
// 人 com.gq.Person@1540e19d
System.out.println("\n人 " + p);
/* 通过外部类实例创建内部类实例 */
Person.Hand hand = p.new Hand();
// 手 com.gq.Person$Hand@677327b6
System.out.println("手 " + hand);
}
}
内部类可以直接访问外部类中的所有成员(即使是 private 成员)
外部类可以直接访问内部类实例中的成员变量和成员方法(即使是 private)
public class Computer {
private int price = 8989;
public static String brand = "苹果";
private void showComputer(Mouse mouse) {
System.out.println("Computer - openComputer()");
System.out.println("mouse.mousePrice = " + mouse.mousePrice);
mouse.printMouse();
}
public class Mouse {
private int mousePrice = 111;
private void printMouse() {
System.out.println("printMouse()_Mouse.mousePrice = " + mousePrice);
}
public void printOuterClassMember() {
System.out.println("\nComputer.price = " + price);
System.out.println("Computer_static_brand: " + brand);
showComputer(this);
}
}
}
class DemoTest {
public static void main(String[] args) {
Computer computer = new Computer();
Computer.Mouse mouse = computer.new Mouse();
/*
Computer.price = 8989
Computer_static_brand: 苹果
Computer - openComputer()
mouse.mousePrice = 111
printMouse()_Mouse.mousePrice = 111
*/
mouse.printOuterClassMember();
}
}
外部类的成员变量和内部类的成员变量重名的时候,如何访问外部类的成员变量?
public class OuterClass {
private int n = 1;
public class InnerClass { /* 内部类 */
private int n = 0;
public void test() {
System.out.println(n); // 0
System.out.println(this.n); // 0
// 访问外部类的 n 属性
System.out.println(OuterClass.this.n); // 1
}
}
}
四、内部类内存布局
✏️ 通过外部类实例创建内部类实例后,内部类实例的内存中会有一个指针指向外部类实例画下面代码的内存布局图:
public class Computer {
public int price = 8989;
public class Mouse { /* 内部类 */
public String color = "黑";
}
}
class DemoTest {
public static void main(String[] args) {
Computer computer = new Computer();
/* 创建内部类实例 */
Computer.Mouse mouse1 = computer.new Mouse();
Computer.Mouse mouse2 = computer.new Mouse();
// computer.price = 8989
System.out.println("\ncomputer.price = " + computer.price);
// mouse.color = 黑
System.out.println("mouse.color = " + mouse1.color);
}
}
五、静态嵌套类(Static Nested Class)
✏️ 静态嵌套类:被static
修饰的嵌套类
✏️ 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中
✏️ 与一般的顶级类不同,静态嵌套类有一些特殊权限
📝 可以直接访问或调用 外部类中的 静态成员(即使是private
)
📝 若想访问或调用 外部类中的 实例成员必须先创建外部类的实例,然后通过外部类的实例访问外部类的实例成员(即使是private
成员)
普通顶级类相比静态嵌套类的区别1:
public class Person {
private String name = "张浩男";
private int age = 16;
private static final char GENDER = '男';
private static String father = "庆医";
private static int familyNo = 9521;
private void play() {
System.out.println("Person_play()");
}
}
✏️ 上面的 Person 类中的属性 name、属性 age 和方法 play 都被private
关键字修饰
✏️ 在其他类中无法访问 Person 类中的私有成员变量或私有方法(如下图)
普通顶级类相比静态嵌套类的区别2(静态嵌套类的好处):
public class Person {
private String name = "张浩男";
private int age = 16;
private static final char GENDER = '男';
private static String father = "庆医";
private static int familyNo = 9521;
private void play() {
System.out.println("Person_play()");
}
public static void run() {
System.out.println("Person_run()");
}
/* 静态嵌套类 */
public static class Phone {
public void showInfo() {
System.out.println("\n----------- showInfo()");
/* 静态嵌套类可直接访问外部类的静态成员(即使是 private 成员) */
System.out.println("GENDER = " + GENDER);
System.out.println("father = " + father);
System.out.println("familyNo = " + familyNo);
run();
System.out.println();
/* 创建外部类实例后, 才可访问外部类的实例成员(即使是 private 成员) */
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
person.play();
System.out.println("----------- showInfo()");
}
}
}
public class TestDemo {
public static void main(String[] args) {
Person.Phone phone = new Person.Phone();
phone.showInfo();
/*
output:
----------- showInfo()
GENDER = 男
father = 庆医
familyNo = 9521
Person_run()
张浩男
16
Person_play()
----------- showInfo()
*/
}
}
六、什么情况使用嵌套类?
✏️ 若类 A 只在类 C 内部被使用,可以考虑把类 A 嵌套到类 C 中
📝 封装性更好
📝 程序包更加简洁
📝 增加可读性和维护性
✏️ 若类 A 需要经常访问类 C 中的非共有成员(被private
修饰的成员),可以考虑将类 A 嵌套到类 C 中
📝 可根据需要将类 A 隐藏起来(不堆外暴露)
✏️ 若需要经常访问非公共的实例成员,设计成内部类(非静态嵌套类),否则设计成静态嵌套类
📝 若必须先有 C 实例才能创建 A 实例,那么可以考虑把 A 设计为 C 的内部类
七、局部类(Local Class)
✏️ 定义在代码块中的类叫局部类(可定义在方法中,for
循环中,if
语句中)
📝 You can define a local class inside any block. You can define a local class in a method body, a for loop, or an if clause.
✏️ 局部类的作用域只在定义它的代码块中
✏️ 局部类中不能定义除编译时常量之外的其他static
成员
📝 编译时常量:若将基本类型或字符串定义为常量(static final
),并且在编译时就能确定值,则编译器会使用常量值替换各处的常量名(类似 C 语言中的宏替换)
📝 被static
修饰的成员可以通过类名访问或调用,但局部类的作用域只在定义它的代码块中,产生矛盾。
✏️ 局部类内部只能访问final
或有效 final 的局部变量
📝 有效 final:虽然没有被final
修饰,但只进行了 一次赋值(若被赋值了不止一次,则不是有效 final)
📝 从 Java8 开始,如果局部变量没有被第二次赋值,则该局部变量会被认定为是【有效 final】
✏️ 局部类可以直接访问外部类中的所有成员(即使是private
成员)
📝 局部类只有定义在实例相关的代码块中才能直接访问外部类中的实例成员(实例变量、实例方法)
public class OuterClass {
private String outerName = "龙行天下";
public void testMethod() {
// 定义在代码块中的变量叫局部变量
int number = 123;
// 若没有第二次给 number 赋值, number 就是有效 final 的局部变量
// number = 5;
final int count = 666;
class LocalClassTest { /* 一个局部类 */
// num 是一个编译时常量
private static final int num = 22;
private void show() {
System.out.println("LocalClassTest_show()");
System.out.println("number = " + number);
System.out.println("count = " + count);
System.out.println("outerName = " + outerName);
}
}
LocalClassTest localClass = new LocalClassTest();
localClass.show();
System.out.println(LocalClassTest.num);
}
}
class TestDemo {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.testMethod();
}
}
🎁 结束:如有错误,请不吝赐教!