学习了继承后,咱们知道,子类能够在父类的基础上改写父类内容,好比,方法重写。那么咱们能不能随意的继承 API中提供的类,改写其内容呢?显然这是不合适的。为了不这种随意改写的状况,Java提供了 final 关键字, 用于修饰不可改变内容ide
final关键字能够修饰:学习
类:被修饰的类,不能被继承。this
方法:被修饰的方法,不能被重写。spa
变量:被修饰的变量,不能被从新赋值code
final修饰的类:不能被继承。对象
格式blog
1 final class 类名 { 2 // 类 3 }
格式继承
1 修饰符 final 返回值类型 方法名(参数列表){ 2 //方法体 3 }
重写被 final 修饰的方法,编译时就会报错接口
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改内存
1 public class Demo01Final { 2 3 public static void main(String[] args) { 4 int num1 = 10; 5 System.out.println(num1); // 10 6 num1 = 20; 7 System.out.println(num1); // 20 8 9 // 一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。 10 // “一次赋值,终生不变” 11 final int num2 = 200; 12 System.out.println(num2); // 200 13 14 // num2 = 250; // 错误写法!不能改变! 15 // num2 = 200; // 错误写法! 16 17 // 正确写法!只要保证有惟一一次赋值便可 18 final int num3; 19 num3 = 30; 20 } 21 }
被final修饰的两种局部变量比较:
1 // 第一种:编译报错 2 final int c = 0; 3 for (int i = 0; i < 10; i++) { 4 c = i; 5 System.out.println(c); 6 } 7 8 9 ---------------------------------------- 10 // 第二种:不报错 11 // 想当与每次都从新定义了一个c变量 12 for (int i = 0; i < 10; i++) { 13 final int c = i; 14 System.out.println(c); 15 }
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。可是不影响对象内部的成员变量值的修改。
1 public class Student { 2 3 private String name; 4 5 public Student() { 6 } 7 8 public Student(String name) { 9 this.name = name; 10 } 11 12 public String getName() { 13 return name; 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 } 20 21 22 --------------------------------------------------------- 23 public class Demo01Final { 24 25 public static void main(String[] args) { 26 27 // 对于基本类型来讲,不可变说的是变量当中的数据不可改变 28 // 对于引用类型来讲,不可变说的是变量当中的地址值不可改变 29 Student stu1 = new Student("赵丽颖"); 30 System.out.println(stu1); 31 System.out.println(stu1.getName()); // 赵丽颖 32 stu1 = new Student("霍建华"); 33 System.out.println(stu1); 34 System.out.println(stu1.getName()); // 霍建华 35 System.out.println("==============="); 36 37 final Student stu2 = new Student("高圆圆"); 38 // 错误写法!final的引用类型变量,其中的地址不可改变 39 // stu2 = new Student("赵又廷"); 40 System.out.println(stu2.getName()); // 高圆圆 41 stu2.setName("高圆圆圆圆圆圆"); 42 System.out.println(stu2.getName()); // 高圆圆圆圆圆圆 43 } 44 }
对于成员变量来讲,若是使用final关键字修饰,那么这个变量也照样是不可变。
1. 因为成员变量具备默认值,因此用了final以后必须手动赋值,不会再给默认值了。
2. 对于final的成员变量,要么使用直接赋值,要么经过构造方法赋值。两者选其一。
3. 若是没有对变量直接赋值。那么必须保证类当中全部重载的构造方法,都最终会对final的成员变量进行赋值。
1 public class Person { 2 3 private final String name/* = "鹿晗"*/; 4 5 public Person() { 6 name = "关晓彤"; 7 } 8 9 public Person(String name) { 10 this.name = name; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 // set方法就无效了,并且this.name这一行会直接报错,final修饰后其变量值就不可在变 18 // public void setName(String name) { 19 // this.name = name; 20 // } 21 }
在Java中提供了四种访问权限,使用不一样的访问权限修饰符修饰时,被修饰的内容会有不一样的访问权限,
public:公共的。
protected:受保护的
default:默认的
private:私有的
public > protected > (default) > private
public | protected | (default) | private | |
同一个类(我本身) | Yes | Yes | Yes | Yes |
同一个包(我儿子【子类】或我邻居【无关类】) | Yes | Yes | Yes | No |
不一样包子类(我儿子) | Yes | Yes | No | No |
不一样包非子类(陌生人) | Yes | No | No | No |
注意事项:(default)并非关键字“default”,而是根本不写。
若是一个事物的内部包含另外一个事物,那么这就是一个类内部包含另外一个类。例如:身体和心脏的关系。又如:汽车和发动机的关系。
将一个类A定义在另外一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
成员内部类 :定义在类中方法外的类。
语法
1 修饰符 class 外部类名称 { 2 修饰符 class 内部类名称 { 3 // ... 4 } 5 // ... 6 }
1 // 外部类 2 public class Body { 3 4 // 成员内部类 5 public class Heart { 6 7 // 内部类的方法 8 public void beat() { 9 System.out.println("心脏跳动:蹦蹦蹦!"); 10 System.out.println("我叫:" + name); // 正确写法! 11 } 12 13 } 14 15 // 外部类的成员变量 16 private String name; 17 18 // 外部类的方法 19 public void methodBody() { 20 System.out.println("外部类的方法"); 21 new Heart().beat(); 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 }
在定义内部类的时候,若是内部类的成员变量和外部重名的时候,范围外部类成员变量:外部类名称.this.外部类成员变量名
1 // 若是出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名 2 public class Outer { 3 4 int num = 10; // 外部类的成员变量 5 6 public class Inner /*extends Object*/ { 7 8 int num = 20; // 内部类的成员变量 9 10 public void methodInner() { 11 int num = 30; // 内部类方法的局部变量 12 System.out.println(num); // 局部变量,就近原则 13 System.out.println(this.num); // 内部类的成员变量 14 System.out.println(Outer.this.num); // 外部类的成员变量 15 } 16 } 17 } 18 19 20 --------------------------------------------------------------------------------- 21 public class Demo02InnerClass { 22 23 public static void main(String[] args) { 24 // 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称(); 25 Outer.Inner obj = new Outer().new Inner(); 26 obj.methodInner(); 27 } 28 }
方法一:间接方式:在外部类的方法当中,使用内部类;而后main只是调用外部类的方法。
方法二:直接方式,公式:类名称 对象名 = new 类名称();
【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
1 public class Body { // 外部类 2 3 public class Heart { // 成员内部类 4 5 // 内部类的方法 6 public void beat() { 7 System.out.println("心脏跳动:蹦蹦蹦!"); 8 System.out.println("我叫:" + name); // 正确写法! 9 } 10 11 } 12 13 // 外部类的成员变量 14 private String name; 15 16 // 外部类的方法 17 public void methodBody() { 18 System.out.println("外部类的方法"); 19 20 // 外部类想要访问内部类须要先建立对象 21 new Heart().beat(); 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 } 32 33 34 --------------------------------------------------- 35 public static void main(String[] args) { 36 Body body = new Body(); // 外部类的对象 37 // 经过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart 38 // 这里虽然没有new内部类,可是在外部类中访问方法的时候已经new了 39 body.methodBody(); 40 System.out.println("====================="); 41 42 // 按照公式写: 43 Body.Heart heart = new Body().new Heart(); 44 heart.beat(); 45 }
若是一个类是定义在一个方法内部的,那么这就是一个局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
语法
1 修饰符 class 外部类名称 { 2 修饰符 返回值类型 外部类方法名称(参数列表) { 3 class 局部内部类名称 { 4 // ... 5 } 6 } 7 }
局部内部类的权限修饰符:
1. 外部类:public / (default)
2. 成员内部类:public / protected / (default) / private
3. 局部内部类:什么都不能写
1 class Outer { 2 3 public void methodOuter() { 4 // 局部内部类 5 class Inner { 6 int num = 10; 7 public void methodInner() { 8 System.out.println(num); // 10 9 } 10 } 11 12 Inner inner = new Inner(); 13 inner.methodInner(); 14 } 15 } 16 17 18 ------------------------------------------------ 19 public class DemoMain { 20 21 public static void main(String[] args) { 22 Outer obj = new Outer(); 23 obj.methodOuter(); 24 } 25 }
局部内部类,若是但愿访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。
备注:从Java 8+开始,只要局部变量事实不变,那么final关键字能够省略。【只要在代码中没有在更改局部变量的值】
缘由:
1. new出来的对象在堆内存当中。
2. 局部变量是跟着方法走的,在栈内存当中。
3. 方法运行结束以后,马上出栈,局部变量就会马上消失。
4. 可是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
1 public class MyOuter { 2 3 public void methodOuter() { 4 int num = 10; // 所在方法的局部变量 5 6 class MyInner { 7 public void methodInner() { 8 System.out.println(num);// num的值不在更改就能够访问 9 } 10 } 11 } 12 }
若是接口的实现类(或者是父类的子类)只须要使用惟一的一次,
那么这种状况下就能够省略掉该类的定义,而改成使用【匿名内部类】。
语法:
1 接口名称 对象名 = new 接口名称() { 2 // 覆盖重写全部抽象方法 3 };
对格式“new 接口名称() {...}”进行解析:
1. new表明建立对象的动做
2. 接口名称就是匿名内部类须要实现哪一个接口
3. {...}这才是匿名内部类的内容
1 public interface MyInterface { 2 3 void method1(); // 抽象方法 4 5 void method2(); 6 7 } 8 9 10 ---------------------------------------------- 11 public class DemoMain { 12 13 public static void main(String[] args) { 14 15 // 使用匿名内部类,但不是匿名对象,对象名称就叫objA 16 MyInterface objA = new MyInterface() { 17 @Override 18 public void method1() { 19 System.out.println("匿名内部类实现了方法!111-A"); 20 } 21 22 @Override 23 public void method2() { 24 System.out.println("匿名内部类实现了方法!222-A"); 25 } 26 }; 27 objA.method1(); 28 objA.method2(); 29 System.out.println("================="); 30 31 // 及使用匿名内部类,又使用匿名对象 32 // 使用了匿名内部类,并且省略了对象名称,也是匿名对象 33 new MyInterface() { 34 @Override 35 public void method1() { 36 System.out.println("匿名内部类实现了方法!111-B"); 37 } 38 39 @Override 40 public void method2() { 41 System.out.println("匿名内部类实现了方法!222-B"); 42 } 43 }.method1(); 44 // 由于匿名对象没法调用第二次方法,因此须要再建立一个匿名内部类的匿名对象 45 new MyInterface() { 46 @Override 47 public void method1() { 48 System.out.println("匿名内部类实现了方法!111-B"); 49 } 50 51 @Override 52 public void method2() { 53 System.out.println("匿名内部类实现了方法!222-B"); 54 } 55 }.method2(); 56 } 57 58 }
匿名内部类和匿名对象的区别:
1. 匿名内部类,在【建立对象】的时候,只能使用惟一一次。
若是但愿屡次建立对象,并且类的内容同样的话,那么就须要使用单独定义的实现类了。
2. 匿名对象,在【调用方法】的时候,只能调用惟一一次。
若是但愿同一个对象,调用屡次方法,那么必须给对象起个名字。
3. 匿名内部类是省略了【实现类/子类名称】,可是匿名对象是省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事!!!
-------------------