Java 内部类、Lambda表达式

 

外部类、接口只能使用public、abstract、final修饰,不能使用private、protected、static修饰,就是说外部类、接口的访问权限只能是默认、public。

局部变量不能使用static修饰,但可以使用final修饰。

 

内部类提供了更好的封装,可以把内部类隐藏在外部类之内。

 

 


 

 

 

普通内部类

修饰符 class Outer{
  修饰符 class Inner{
    .......
  }
}
普通内部类相当于外部类的一个成员,可以使用任何修饰成员的修饰符。内部类中可以有构造函数、初始化块、成员变量、成员方法。

内部类内(内部类成员级、内部类方法体中均)可以直接访问外部类的成员。

在外部类中访问内部类的成员:
外部类的成员方法中不能直接访问内部类中的成员,需要new Inner()创建内部类对象,再通过内部类对象访问内部类的成员。

在外部类外访问内部类的成员:
外部类类名.内部类名  变量名=外部类实例对象.new   内部类(参数表); //需要先创建内部类实例,再通过内部类实例访问内部类的成员
外部类类名.内部类名  变量名=new  外部类(参数表) .new 内部类(参数表); //内部类是public、要访问的内部类成员时public才可以访问
内部类的完整类名是:外部类类名.内部类类名,有时候还需要加前缀包名。


会编译为两个.class文件,内部类一个,外部类一个。
创建外部类对象时,在heap中会有两个对象,一个外部类的,一个内部类的,二者分开存储,但内部类中保存了对应的外部类的引用。

当外部类成员、内部类成员、内部内的局部变量同名时,在内部类的方法体中:
this 表示当前内部类对象
外部类类名.this 表示当前外部类对象

 

 


 

 

 

静态内部类

修饰符 class Outer{
  static class Inner{
  .......
  }
}
该内部类属于外部类本身,不属于外部类的任何一个实例。静态内部类也是外部类的一个成员。
静态内部类中可以包含静态成员,也可以包含普通成员。static只是表示此内部类是外部类的类成员,并不是说此内部类只能包含静态成员。
静态内部类中只能访问外部类的静态成员,不能访问外部内的非静态成员,但可以访问内部类自身的所有成员。

在外部类中访问内部类的成员:
需要先 new 内部类类名(参数表) 创建内部类对象,再通过该对象来访问。
内部类的静态成员可通过内部类类名直接访问。

在外部类外访问内部类的成员:
外部类.内部类 变量名=new 外部类.内部类(参数表); //通过外部类类名直接调用内部类的构造函数(内部类使用static修饰),参数表是内部类构造函数的参数表
内部类的静态成员可直接通过 外部类类名.内部类类名.静态成员 调用。
外部类.内部类 也是一种数据类型,表示内部类。

 

 


 

 

 

局部内部类

定义在方法体中的内部类,和局部变量一个级别。

局部变量不能使用任何访问权限、static修饰,但可以使用final来修饰,所以局部内部类也不能使用这些来修饰,但局部内部类可以使用final来修饰。

局部内部类中的成员,和正常的成员一样,可以使用一切修饰符。

局部内部类的上一级程序单元是所在方法,不是类。

在局部内部类中,只能访问所在方法体中的局部变量(只能在成员一级访问,比如用来给内部类的成员变量赋值,不能在内部类的方法中访问),不能访问外部类的成员。

 

 


 

 

 

匿名内部类

new 接口名|父类构造器(实参表){
........
}
匿名内部类没有类名,只能使用一次。会在定义处创建该类的一个实例。
实质还是定义一个类,要实现一个接口,或继承+重写一个父类。但最多只能实现一个接口,或继承+重写一个父类。
因为没有类名,所以没有构造函数,但可以有初始化块。

如果匿名内部类是外部类的成员级,则内部类中可以访问外部类的成员,比如:
private ActionListener al=new ActionListener{
.............
}

如果匿名内部类是局部变量级的,则匿名内部类中不能访问外部内的成员,比如:
public void xxx(){
  ........
  btn.addActionListener(new ActionListener{
  .......
  });
}

 

 


 

 

 

注意:

外部类成员级的内部类,在内部类的成员级、方法体内均可直接访问外部类的成员。

局部变量级的内部类,只能在内部类的成员级访问外部类的成员,在内部类的方法体内不能访问外部类的成员。

 

 


 

 

 

Lambda表达式

函数式接口:

只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能包括一个抽象方法。

Java8提供了@FunctionalInterface注解,该注解放在接口定义前面,表示该接口是函数式接口,编译时会检查该接口是否符合函数式接口的要求。

 

Lambda表达式可以实现函数式接口,并在实现处创建一个对象,和匿名内部类相似。

区别:匿名内部类可以实现任何接口、抽象类,但Lambda表达式只能实现函数式接口。Lambda表达式相当于一种特殊的匿名内部类,但Lambda表达式比匿名内部类更加简洁。

 

Lambda表达式只是语法糖,编译时会被替换为常规的代码。

 

 

Lambda表达式语法:

(形参表) -> {

  .......//抽象方法的实现部分

}

无形参时,写为();只有一个形参时,可以缺省()。

代码块中只有一条语句时,可以缺省{};代码块中只有一条return语句时,可以缺省{}和return关键字(要缺省,必须同时缺省return关键字和{})。

参数表只写形参变量名,不写形参类型。

 

 

示例:

 1 package test;
 2 
 3 interface  Interface1{
 4     //无形参、无返回值
 5     void print();
 6 }
 7 
 8 interface Interface2{
 9     //有形参
10     void print(String str);
11 }
12 
13 interface Interface3 {
14     //有返回值
15     String getString();
16 }
17 
18 public class Test {
19     public static void main(String[] args) {
20         Interface1 interface1=()->{
21             System.out.println("接口1 ok");
22         };
23         interface1.print();
24 
25         //只有一个形参时,可以缺省();代码块只有一条语句时,可以缺省{}
26         Interface2 interface2=str-> System.out.println(str);
27         interface2.print("接口2 ok");
28 
29         //方法体中只有一条return语句时,可以同时缺省{}和return关键字,要缺省就必须同时缺省这两部分
30         Interface3 interface3=()-> "接口3 ok";
31         String str=interface3.getString();
32         System.out.println(str);
33     }
34 }

 

Lambda表达式会在该处创建一个该接口的示例,并返回这个实例,即Lambda表达式的值是对应接口的实例。

 

可以将Lambda表达式作为实参传递给方法:

 1 package test;
 2 
 3 interface Interface3 {
 4     //有返回值
 5     String getString();
 6 }
 7 
 8 public class Test {
 9     public static void main(String[] args) {
10         //可以将Lambda表达式作为实参传递给方法。Lambda表达式的值是对应接口的实例,并不是所实现抽象方法的返回值String。Lambda表达式比匿名内部类更简洁。
11         printStr(()->"ok");
12     }
13 
14     public static void printStr(Interface3 interface3){
15         System.out.println(interface3.getString());
16     }
17 }

 

 

Java中的很多接口都是函数式接口,比如ActionListener接口、Runnable接口。

 

Lambda表达式可访问的变量和匿名内部类可访问的变量完全一致,但Lambda表达式不能调用接口中定义的默认方法,匿名内部类则可以。

 

posted @ 2019-05-12 16:12  chy_18883701161  阅读(527)  评论(0编辑  收藏  举报