1、笔记
1、局部内部类 如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。 “局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。 定义格式: 修饰符 class 外部类名称 { 修饰符 返回值类型 外部类方法名称(参数列表) { class 局部内部类名称 { // ... } } } 小节一下类的权限修饰符: public > protected > (default) > private 定义一个类的时候,权限修饰符规则: 1. 外部类:public / (default) 2. 成员内部类:public / protected / (default) / private 3. 局部内部类:什么都不能写 ---> 跟(default)不一样 2、局部内部类访问方法的局部变量 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。 备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。 原因: 1. new出来的对象在堆内存当中。 2. 局部变量是跟着方法走的,在栈内存当中。 3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。 4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。 3、匿名内部类 如果接口的实现类(或者是父类的子类)只需要使用唯一的一次, 那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。 匿名内部类的定义格式: (接口名称 对象名 = )new 接口名称() { // 覆盖重写所有抽象方法 }; ---> ";"不可省略 对格式“new 接口名称() {...};”进行解析: 1. new代表创建对象的动作 2. 接口名称就是匿名内部类需要实现哪个接口 3. {...}这才是匿名内部类的内容 另外还要注意几点问题: 1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。 ---> 指每个方法只能使用一次 如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。 2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。 ---> 类似只有一个方法的匿名内部类 如果希望同一个对象,调用多次方法,那么必须给对象起个名字。 3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】 强调:匿名内部类和匿名对象不是一回事!!! 4、类(类比String)和接口都可以作为成员变量类型,标准写法就是把 实现类对象 传进去,同时接口还可以传进去 匿名内部类和匿名对象 5、接口可以作为方法的参数或返回值 6、静态内部类的访问 class Demo1_InnerClass { public static void main(String[] args) { //外部类名.内部类名 对象名 = 外部类名.内部类对象; Outer.Inner oi = new Outer.Inner(); // new Outer.Inner() 相当于 Outer.new Inner(),new 是作用在Inner()上的(内部类对象) oi.method(); //内部类里面静态方法的访问特点:外部类.内部类.静态方法名(); Outer.Inner2.print(); } } class Outer { static class Inner { public void method() { System.out.println("method"); } } static class Inner2 { // 静态内部类的静态方法的访问 public static void print() { System.out.println("print"); } } } 7、局部内部类访问局部变量必须用final修饰 局部内部类在访问他所在方法中的局部变量必须用final修饰,为什么? 因为当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用 但是jdk1.8取消了这个事情,所以我认为这是个bug,虽然取消,如果在书写代码时候,没有手动添加,系统底层也会默给你final 8、匿名内部类【重点】 写在方法里面 * A:匿名内部类 * 就是内部类的简化写法。 * B:前提:存在一个类或者接口 * 这里的类可以是具体类也可以是抽象类。 * C:格式: * new 类名或者接口名(){ 重写方法; } * D:本质是什么呢? * 是一个继承了该类或者实现了该接口的子类匿名对象。 interface Inter { public void print(); } class Outer { class Inner implements Inter { public void print() { System.out.println("print"); } } public void method(){ //Inner i = new Inner(); //i.print(); //new Inner().print(); //Inter i = new Inner(); //父类引用指向子类对象 new Inter() { //实现Inter接口 public void print() { //重写抽象方法 System.out.println("print"); } }.print(); // 得到子类对象,直接调用print() } } interface Inter { public void show1(); public void show2(); } //匿名内部类只针对重写一个方法时候使用 class Outer { public void method() { // 方式一: 调用一次方法,重写一次接口方法 缺点: 麻烦 /*new Inter(){ public void show1() { System.out.println("show1"); } public void show2() { System.out.println("show2"); } }.show1(); new Inter(){ public void show1() { System.out.println("show1"); } public void show2() { System.out.println("show2"); } }.show2();*/ //方式二: 父类引用(Inter i)指向子类对象(new Inter()) 缺点: 不能写自己的方法 Inter i = new Inter(){ public void show1() { System.out.println("show1"); } public void show2() { System.out.println("show2"); } /*public void show3() { System.out.println("show3"); }*/ }; i.show1(); i.show2(); //i.show3(); // 不能访问自己写的方法 //匿名内部类是不能向下转型的,因为没有子类类名 //总结: 匿名内部类只针对重写一个方法时候使用 } } 9、匿名内部类在开发中的应用 //如何调用PersonDemo中的method方法呢? //第一种方法 class Test1_NoNameInnerClass { public static void main(String[] args) { PersonDemo pd = new PersonDemo (); pd.method(new Student()); } //这里写抽象类,接口都行 abstract class Person { public abstract void show(); } class PersonDemo { public void method(Person p) { p.show(); } } class Student extends Person { public void show() { System.out.println("show"); } } //第二种方式 匿名内部类作为参数传递 class Test1_NoNameInnerClass { public static void main(String[] args) { PersonDemo pd = new PersonDemo(); //第二种方案 : 匿名内部类的用法:作为参数传递 pd.method(new Person(){ public void show() { System.out.println("这是匿名内部类的show方法..."); } }); } } //这里写抽象类,接口都行 abstract class Person { public abstract void show(); } class PersonDemo { //当你看到一个方法的形式参数是一个类名的时候,这里其实需要的该类的对象 //补充: 当你看到一个方法的形式参数是一个抽象类类名或者是接口名的时候,这里其实需要的是该抽象类的子类对象或者该接口的实现类对象 public void method(Person p) { p.show(); } } 10、 A:代码块概述 在Java中,使用{}括起来的代码被称为代码块。 B:代码块分类 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。 C:常见代码块的应用 a:局部代码块 在方法中出现;限定变量生命周期,及早释放,提高内存利用率 b:构造代码块 (初始化块) 在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行 c:静态代码块 在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。 一般用于加载驱动 11、 在静态方法中是没有this关键字的 - 如何理解呢? - 静态是随着类的加载而加载,this是随着对象的创建而存在。 - 静态比对象先存在。 12、 重写:子父类出现了一模一样的方法(注意:返回值类型可以是子父类) class Demo4_Override { public static void main(String[] args) { } } class Person { public void print() { System.out.println("Person"); } } class Student extends Person { public void print() { System.out.println("Student"); } } class Father { public Person method() { return new Person(); } } class Son extends Father { public Student method() { //返回值类型可以是子父类 return new Student(); } } 13、方法重写注意事项 - a:父类中私有方法不能被重写 - 因为父类私有方法子类根本就无法继承 - b:子类重写父类方法时,访问权限不能更低 - 最好就一致 - c:父类静态方法,子类也必须通过静态方法进行重写 - 其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解(静态只能覆盖静态) - 子类重写父类方法的时候,最好声明一模一样。 14、 成员变量 - 编译看左边(父类),运行看左边(父类)。 成员方法 - 编译看左边(父类),运行看右边(子类)。 静态方法 - 编译看左边(父类),运行看左边(父类)。 - (静态和类相关,算不上重写,所以,访问还是左边的) - 只有非静态的成员方法,编译看左边,运行看右边 15、 多态的好处 - a:提高了代码的维护性(继承保证) - b:提高了代码的扩展性(由多态保证) (例子中参数只需 method(Animal a), 就可以接收 Cat c) 多态的弊端 - 不能使用子类的特有属性和行为。 16、接口成员特点 - 成员变量;只能是常量,并且是静态的并公共的。 - 默认修饰符:public static final - 建议:自己手动给出。 - 构造方法:接口没有构造方法。 - 成员方法:只能是抽象方法。 - 默认修饰符:public abstract - 建议:自己手动给出。 17、抽象类和接口的区别 A:成员区别 - 抽象类: - 成员变量:可以变量,也可以常量 - 构造方法:有 - 成员方法:可以抽象,也可以非抽象 - 接口: - 成员变量:只可以常量 - 成员方法:只可以抽象 B:关系区别 - - 类与类 - 继承,单继承 - 类与接口 - 实现,单实现,多实现 - 接口与接口 - 继承,单继承,多继承 C:设计理念区别 - 抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。 - 接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。 18、如何编译运行带包的类 - a:javac编译的时候带上-d即可(自动创建包名) - javac -d . HelloWorld.java - b:通过java命令执行。 - java 包名.HellWord 19、封装 隐藏实现细节,只提供接口访问。 封装是相对的。