开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
1.final:
- 被 final 修饰的类:不能被继承。
- 被 final 修饰的方法:不能被重写。
- 被 final 修饰的变量:变量变为常量,只能进行一次赋值。
这是非常好的一个东西。可以避免我们造出混乱的继承结构。比如Java中的String类就是final的,它是不能被继承的。
final不能修饰抽象类或接口,因为定义抽象类或接口的目的就是为了让其他类继承或实现。
1.1.常量:
public final double PI = 3.14; // 只是不能被再次赋值,也不算是常量
public static final double PI = 3.14; // 公共常量(被static修饰,内存中只有此一份)
private static final double PI = 3.14; // 私有常量
如果将基本类型或字符串定义为常量,且在编译时就能确定值。
编译器会使用常量值替代各处常量名(类似 c 语言的宏替换)
上面的例子是编译时能确定值的,下面举例编译时不能确定值的情况。
static final int PI = getPI();
小测试:
public static void main(String args[]) {
String a = "go die, ";
final String b = "final";
String c = "go die, " + b; // go die, final
String d = a + b; // go die, final
String e = "go die, final";
System.out.println(e == c); // true,比较两个变量是否指向同一个对象
System.out.println(e == d); // false
// 如果b不加final,那么结果就是false、false
}
解释:
在编译阶段,变量c其实已经是"go die, final"了,所以c和e其实是同一个对象。
但是d却是运行时生成的,并不引用常量池中的"go die, final"这个字符串,所以e和d并不是同一个对象。
究其根本原因,还是在于b 在编译阶段就已经被当作常量“final” 去做下面的编译了。
2.嵌套类:定义在一个类中的类
//最外层的外部类,又称顶级类
public class OuterClass{
// 静态嵌套类
static class StaticNestedClass{
}
// 内部类(非静态嵌套类)
class InnerClass{
}
}
2.1.内部类:没有被 static 修饰的嵌套类。 (非静态内部类)
一般情况下,类与类之间相互独立。内部类就是打破这种独立,让一个类成为另一个类的内部成员,和成员变量、成员方法同等级别。
1.非静态内部类跟实例变量、实例方法一样,都必须通过外部类的对象才能调用。
public class Persion{
//Persion对象不实例化 这个实例变量就没用
private int age;
public int getAge(){
return age;
}
//Persion对象不实例化 这个内部类就没用(没法创建)
class Hand{
}
}
Person person = new Persion();
Hand hand = person.new Hand(); //必须通过对象的引用去创建
2.内部类不能定义除编译时常量以外的任何 static 成员。
public class OuterClass{
class InnerClass{
// 不能定义静态成员
public static int a = 10;
//可定义编译时常量
private static final int PI = 3.14;
}
}
3.内部类可以直接访问外部类中的所有成员(即使被声明为 private)
4.外部类可以直接访问内部类实例的成员变量、方法(即使被声明为 private)
5.内部类细节
public class Outer{
private int x = 1;
public class Inner{
private int x = 2;
public void show(){
print(x); // 2
print(this.x); // 2
print(Outer.this.x); // 1
}
}
}
6.为什么要使用内部类:采用内部类技术,可以隐藏细节和内部结构,封装性更好,让程序的结构更加合理。
2.2.静态嵌套类(静态内部类):
1.静态嵌套类在行为上是个顶级类,只是定义的代码写在了一个类中。
public class Person{
public static class Car{
}
}
//调用(Car与Person无任何关系,只是恰巧代码在Person类里面而已)
Person.Car car = new Persion.Car();
2.静态嵌套类可直接访问外部类除实例变量、实例方法外的其他成员(即使被声明为 private)
// 从类加载角度分析以下代码原因
public class Persion{
private int age;
private static int count = 0;
public static class Car{
public void run(){
print(age); //错误:不可直接访问外部类的实例变量、实例方法
Persion.count = 1; //可以不通过setter方法就能访问其外部类的private属性
}
}
}
什么情况下使用嵌套类?
- 如果类 A 只在类 C 内部会被用到,可以考虑将类 A 嵌套到类 C 中。
- 如果类 A 需要经常访问类 C 的非公共成员,可以考虑将类 A 嵌套到类 C 中。
- 如果需要经常访问非公共的实例成员,设计成内部类,否则设计成静态嵌套类。
- 所以设计内部类的时候,能静态就静态。
2.3.局部类:Local Class(局部内部类)
局部类:定义在代码块中的类(可以定义在方法中、for 循环中、if 语句中等)
- 局部类不能定义除编译时常量以外的任何 static 成员。(因为代码块里面的东西作用范围仅在代码块中有效)
- 局部类只能访问 final 或 有效 final 的局部变量。
- 局部类可直接访问外部类中的所有成员(即使被声明为 peivate)
public class Persion{
private int age;
public void test(){
//java8开始,若局部变量没有被第二次赋值,就认定为是有效final
int num = 10;
class A{
// static int age = 1; //不可定义
void a(){
print(num);
print(age);
}
}
}
}
3.抽象类(Abstract Class):被 abstract 修饰的类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
- 可以定义为抽象方法
- 可自定义构造方法(可通过 new 来创建)
- 子类必须实现抽象父类中的所有抽象方法(除非子类也是抽象类)
- 也可像普通类一样定义成员变量、常量、非抽象方法、嵌套类型、初始化块等
即抽象类中可完全不定义抽象方法。
抽象类意义:
- 用于拓展对象的行为功能:即重用 + 扩展
public abstract class Abs{
public int age;
public Abs(){
}
public void test1(){
}
public abstract void test2();
public abstract void test3();
}
public class Test extends Abs{
//子类必须实现抽象父类中的所有抽象方法
public void test2(){
}
public void test3(){
}
// 子类的构造方法,必须要先调用父类的构造方法
// 所以抽象类虽然不能实例化,但其构造方法的意义是供子类调用。
public Test(){
}
}
抽象类使用场景:
- 抽取子类的公共实现到抽象父类中,并且把要求子类必须要单独实现的方法定义成抽象方法。