1、final
最终的
- 修饰类:表示类不可以被继承
- 修饰方法:表示方法不可被子类覆盖,但是可以重载
- 修饰变量:表示变量一旦赋值就不可以更改它的值
(1)修饰成员变量
- 修饰类变量(静态变量):只能在声明该类变量时指定初始值或者静态代码块中指定初始值
- 修饰成员变量:可以在声明时、普通代码块中、构造方法里指定初始值
(2)修饰局部变量
系统不会为局部变量初始化,在使用前必须指定初始值,也可以直接在声明的时候指定初始值,而无论哪种方法指定初始值,后面都不能更改这个值
public class FinalTest {
// 在声明时赋值,或者在静态代码块中赋值
final static int a = 1;
/**
static { // 静态代码块必须在声明的后面
a = 1;
}
*/
// 在声明时赋值,或者在普通代码块中赋值,或者在构造器中赋值
final int b = 2;
// 成员变量不能在静态代码块中复制,因为会先加载静态代码块,再加载成员变量
/**
{
b = 2;
}
public FinalTest(int b) {
this.b = b;
}
*/
public static void main(String[] args) {
final int localA; // 可以在声明时赋值,也可以后续赋值
localA = 3; // 但是不允许第二次赋值
System.out.println(localA);
}
}
(3)修饰基本数据类型和引用数据类型
- 修饰基本数据类型:数值一旦初始化就不能更改
- 修饰引用数据类型:初始化后不能再指向另外一个对象,但是引用的值可以更改
public class FinalReferenceTest {
public static void main(String[] args) {
final int[] arr = {1,2,3,4};
arr[2] = 10; // 合法
// arr = null; // 非法
final Animal a1 = new Animal("老虎");
a1.setName("狮子"); // 合法
// p = null; // 非法
// p = new Animal("豹子");// 非法
}
}
为什么局部内部类和匿名内部类只能访问局部final变量
局部内部类和匿名内部类在编译之后会生成两个class文件,Test.class、Test1.class
public class Test {
// 局部final变量
public void test(final int b) {
final int a =34; // 局部变量
// 匿名内部类
new Thread() {
public void run() {
System.out.println(a);
System.out.println(b);
}
}.start();
}
}
public class Test {
// 成员变量
private int age = 23;
// 局部final变量
public void test(final int b) {
final int a =34; // 局部变量
// 局部内部类
class InClass {
public void inPrint() {
System.out.println(a);
System.out.println(b);
System.out.println(age); // 可以访问成员变量
}
}
new InClass().inprint();
}
}
注意:内部类和外部类是处于同一级别的,不会因为方法执行完毕而被销毁
这里就会产生一个问题:那就是当外部类的方法执行完后,局部变量就会销毁,但是内部类还在使用,所以就产生了矛盾,为了解决这个问题,就将局部变量复制一份作为内部类的成员变量,这样即使外部类方法中的局部变量销毁了,内部类依然能使用;匿名内部类也是同样
但是这里又产生了一个新问题,既然是复制的局部变量作为内部类的成员变量,那如果局部变量的值被更改了,而内部类却依然在使用之前复制过来的值,这就会造成数据不一致的问题,那怎么解决呢?
那就是将局部变量设置为final,对它初始化后,不能更改它的值,这样就保证了数据的一致性。
2、public、protected、default、private
2.1、作用域
修饰符 | 同一个类 | 同一个包 | 不同包(子类) | 不同包(非子类) |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
private:被private修饰的方法和变量只能在类内部使用
default:不加任何修饰符的方法和变量默认为default,只能在类内部使用或者在同一个包下使用
protected:被protected修饰的方法和变量可以在类内部使用,也可以在子类里面使用(子类与父类之间同包或不同包都行),只是不能在不同包下的非子类使用
public:作用域最广,基本在哪都能用
2.2、测试
包结构:
类(PDTest):
package p1;
import p2.Father2;
public class PDTest1 {
public static void main(String[] args) {
System.out.println("===同一个包下(非子类)====");
Father f = new Father();
System.out.println("a: " + f.a);
System.out.println("b:" + f.b);
f.printDefault();
f.printProtected();
System.out.println("===同一个包下(子类)====");
Child c = new Child();
c.pringFather();
System.out.println("===不同一个包下(非子类)====");
Father2 f2 = new Father2();
// System.out.println("a2: " + f2.a2);
// System.out.println("b2:" + f2.b2);
// f2.printDefault2();
// f2.printProtected2();
// 无论是protected还是default都不可以访问
System.out.println("无论是protected还是default都不可以访问");
System.out.println("===不同一个包下(子类)====");
Child2 c2 = new Child2();
c2.pringFather2();
}
}
// 父类
class Father {
int b;
protected int a;
protected void printProtected() {
System.out.println("Protected: " + a);
}
void printDefault() {
System.out.println("Default: " + b);
}
}
// 子类
class Child extends Father {
public void pringFather() {
super.printDefault();
super.printProtected();
System.out.println("super.b:" + super.b);
System.out.println("super.a:" + super.a);
}
}
//子类2
class Child2 extends Father2{
public void pringFather2() {
// super.printDefault2(); // 不同包下的子类不能使用Default
super.printProtected2();
// System.out.println("super.b2:" + super.b2);
System.out.println("super.a2:" + super.a2);
}
}
Father2:
package p2;
// 父类2
public class Father2 {
int b2;
protected int a2;
protected void printProtected2() {
System.out.println("Protected2: " + a2);
}
void printDefault2() {
System.out.println("Default2: " + b2);
}
}
执行结果:
在同一个包下,无论是不是子类都可以使用protected和default修饰的方法及变量
在不同包下,非子类对象无论是default还是protected都无法访问,而子类可以访问protected,无法访问default