1、内部类
/* * 内部类:声明在一个类的内部的类。之前没有声明 * 在另外一个类的内部的类,称之为顶层类。包含内部 * 类的类,称为外围类。 * * 顶层类可以声明为public,默认访问权限。 * 内部类可以是任意的访问权限。 * * 内部类的作用: * 1 可以令内部类与外围类关系更在紧密。 * 2 内部类可以提供更好地访问权限的控制。 * * 内部类与外围类可以自由的访问彼此的成员。( * 包括声明为private的成员。) * * 内部类可以分为以下几种: * 1 静态成员类 * 2 实例成员类 * 3 局部类 * 4 匿名类 * * */ package day10; public class Inner { private int x; public void k() { T t = new T(); t.y = 2; } class T { private int y; public void f() { x = 1; } } }
2、静态成员类
/* * 静态成员类 * 静态成员类使用static修饰。 * 静态成员类类似于类中声明的静态成员变量。 * 静态成员类不依赖于外围类的对象而存在。在访问静态成员类时, * 不需要创建外围类的对象。 * 在外围类的内部,可以直接通过静态成员类的名字对静态成员类 * 进行访问。 * 在外围类的外部,需要使用外围类.静态成员类来访问静态成员类。 * 静态成员类不能访问外围类的实例成员。(类似于静态方法不能 * 访问实例成员) */ package day10; public class StaticInner { static int x; int y; //静态成员类 public static class T { public void f() { //错误,不能访问外围类的实例成员。 //y = 1; } } public static void f() { //StaticInner.x = 1; x = 1; T t = new T(); //StaticInner.T t = new StaticInner.T(); } } class Outer { public void f() { //x = 3; StaticInner.x = 3; StaticInner.T t = new StaticInner.T(); t.f(); } }
3、实例成员类
/* * 实例成员类 * 实例成员类作为外围类的成员,不使用static修饰。 * 实例成员类类似实例成员变量。 * 实例成员类依赖于外围类的对象,如果我们要创建实例 * 成员类的对象,则必须首先创建外围类的对象。 * 实例成员类可以访问外围类的实例成员。(静态成员类不能) * 实例成员类不能声明任何静态上下文环境。(静态成员变量, * 静态方法,静态初始化块) (静态成员类可以) * 例外:实例成员类可以声明静态编译时常量。(final) * 因为在编译过后,字节码直接使用的是静态final的常量值。 * 而不存在该静态成员变量。 */ package day10; public class InstanceInner { int x = 1; // 实例成员类 public class T { //错误,实例成员类不能声明静态成员。 //static int staX = 5; //例外,可以声明静态的编译时常量。 static final int staX = 5; public void kk() { x = 5; System.out.println(staX * 2); //System.out.println(5 * 2); } } public void f() { x = 2; T t = new T(); } public static void g() { // x = 2; // T t = new T(); } } class Outer2 { public void f() { // InstanceInner i = new InstanceInner(); // InstanceInner.T t = i.new T(); // 如果不需要外围类的引用,可以这样写: InstanceInner.T t = new InstanceInner().new T(); } }
4、案例
/* * */ package day10; public class Shadow { int x = 50; class Inner { int x = 100; public void f() { int x = 200; // 访问局部变量x。 System.out.println(x); // 访问实例成员类的成员变量x。 System.out.println(this.x); // 如何访问外围类的x? // 访问外围类的成员变量x。 System.out.println(Shadow.this.x); } } public static void main(String[] args) { Shadow s = new Shadow(); Inner i = s.new Inner(); i.f(); } }
5、局部类
/* * 局部类:声明在方法,语句块等局部范围内的类。 * 局部类类似于方法中声明的局部变量。 * 局部类不能使用static修饰,也不能使用访问修饰符修饰 * (因为局部变量不能使用以上关键字修饰)。 * 局部类不能声明静态成员。 * 如果局部类处于静态的上下文环境中,则局部类不能访问 * 外围类的实例成员。(可以访问静态成员。) * 如果局部类处于实例的上下文环境中,则局部类可以访问 * 外围类静态与实例的成员。 */ package day10; public class Local { int x = 1; static int y = 1; public void f() { // 局部类 class Inner2 { public void g() { System.out.println(x); System.out.println(y); } } Inner2 i = new Inner2(); } public void k() { //Inner2 i = new Inner2(); } public static void staticF() { class Inner { public void g() { // System.out.println(x); System.out.println(y); } } } }
6、局部类
/* * 局部类仅在声明的位置开始,到其所在的最小语句块结束。 * 局部类的对象在局部范围(声明局部类的范围,即局部类的作用域) * 之外不能使用,为了能够让局部对象存活在局部范围之外,通过 * 令局部类实现一个接口,然后在方法中返回接口类型。也就是 * 将局部类的对象作为接口类型返回。 * */ package day10; public class Local2 { public Mobile get() { class Inner implements Mobile { @Override public void call() { System.out.println("打电话"); } } // Inner i = new Inner(); // return i; return new Inner(); } public static void main(String[] args) { Local2 l = new Local2(); Mobile m = l.get(); m.call(); } } interface Mobile { void call(); } class UseMobile { public void use() { Local2 l = new Local2(); Mobile m = l.get(); m.call(); } }
/* * 局部类对局部变量的访问 * 在JavaSE 8之前,局部类只能访问声明为final的 * 局部变量。 * 从JavaSE 8开始,局部类也能访问声明为非final的 * 局部变量,前提是,该局部变量的值,没有发生更改。 * (final等效的局部变量) */ package day10; public class Local3 { public void f() { int x = 1; // x = 2; class Inner { public void g() { System.out.println(x); } } } }
7、匿名类
/* * 当局部类只用到一次,并且局部类的类体相对较短时, * 我们可以使用匿名类来代替局部类,这样可以是程序 * 更加简洁。 */ package day10_2; public class Anonymous2 { public Mobile getMobile() { // class T implements Mobile { // @Override // public void call() { // System.out.println("打电话"); // } // } // return new T(); return new Mobile() { @Override public void call() { System.out.println("匿名类打电话"); } }; //T t = new T(); //T t2 = new T(); } } interface Mobile { void call(); }
/* * 匿名类 * 匿名类就是没有名字的类。 * 匿名类集类的声明与创建对象为一体,即在声明类的同时 * 创建匿名类的对象。 * * 对于匿名类的语法,new T() {}创建的并不是T类型的对象, * 而是T的子类型的对象(匿名类类型的对象)。如果T是一个类, * 则匿名类继承T。如果T是一个接口,则匿名类实现接口T。 * * 在匿名类中,不能声明构造器。因为匿名类没有名字。 */ package day10_2; public class Anonymous { public void f() { T t = new T(); //匿名类 T t2 = new T() { int x; public void g() { } }; Inter inter = new Inter() { }; A a = new A() { }; //inter = new Inter(); //a = new A(); } } class T { } interface Inter { } abstract class A { }
8、
内部类的字节码文件命名
* 与顶层类一样,在编译过后,内部类也会生成字节码文件(.class)。
* 对于成员类(静态成员类与实例成员类),生成的class文件名
* 为--外围类$成员类.class
* 对于局部类,生成的class文件名为--外围类$数字局部类.class
* 对于匿名类,生成的class文件名为--外围类$数字.class
9、函数式接口
/* * 函数式接口 * 如果接口中有且仅有一个抽象的方法(这里的抽象方法不包括 * 从Object类中继承的方法),则这样的接口 * 称为函数式接口。对于默认方法与静态方法,没有要求。 * * 我们可以使用@FunctionalInterface来对一个接口进行标记, * 表示该接口为函数式接口。 */ package day10_2; @FunctionalInterface public interface Function { void f(); } //@FunctionalInterface interface Function2 { void f(); void g(); } @FunctionalInterface interface Function3 { void f(); String toString(); } class Function3Impl implements Function3 { @Override public void f() { } }
10、Lambda表达式
/* * Lambda表达式的结果类型称为目标类型。 * Lambda表达式的目标类型为函数式接口类型。 * Lambda表达式的结果值就是一个实现了函数式接口类型的对象。 * Lambda表达式在形式上类似于一个匿名的方法。 * * Lambda表达式的语法: * (参数列表) -> {方法体} * 1 参数列表的类型可以省略。 * 2 当参数个数只有一个时,可以省略小括号。 * 3 当方法含有返回值,并且方法体只有一条语句时, * 可以将return与{}一同省略。 * 4 当方法没有返回值,并且方法体只有一条语句是, * 可以省略{}。 */ package day10_2; public class Lambda { public Mobile getMobile() { /* * return new Mobile() { * * @Override public void call() { System.out.println("打电话"); } }; */ // Mobile m = () -> System.out.println("Lambda的实现"); // return m; return () -> System.out.println("Lambda的实现"); } public void f() { Value v = new Value() { @Override public int get(int k) { return 0; } }; //Value v2 = (int k) -> {return 0;}; //省略参数列表的类型。 //Value v2 = (k) -> {return 0;}; //省略小括号 //Value v2 = k -> {return 0;}; //省略{}与return。 Value v2 = k -> 0; } } @FunctionalInterface interface Value { int get(int k); }
11、方法引用
/* * 方法引用 * 当Lambda表达式中仅仅调用一个已经存在的方法时, * 我们就可以使用方法引用来代替Lambda表达式, * 这样能够使程序更加简洁。 * * 方法引用可以分为以下四类: * 1 引用静态方法 * 2 通过对象引用实例方法 * 3 通过类型引用实例方法 * 4 引用构造器 */ package day10_2; public class MethodReference { public void test() { Friend f = (p1, p2) -> { Person.makeFriend(p1, p2); }; /* * 引用静态方法。抽象方法中的参数会依次传递 给所引用方法。 */ f = Person::makeFriend; // f.makeFriend(p1, p2); Tool tool = new Tool(); f = (p1, p2) -> { tool.makeFriend(p1, p2); }; /* * 通过对象引用实例方法,抽象方法中的参数会依次传递 给所引用的方法。 */ f = tool::makeFriend; f = (p1, p2) -> { p1.makeFriend2(p2); }; /* * 通过类型引用实例方法。抽象方法的第一个参数作为 调用方法(所引用的方法)的对象(引用),从第二个 参数起,依次传递给所引用的方法。 */ f = Person::makeFriend2; Create c = (n, a, h, w) -> { return new Person(n, a, h, w); }; /* * 引用构造器。抽象方法的参数会依次传递给构造器。 */ c = Person::new; } } @FunctionalInterface interface Friend { void makeFriend(Person p1, Person p2); } @FunctionalInterface interface Create { Person get(String name, int age, int height, int weight); } class Tool { public void makeFriend(Person p1, Person p2) { // 实现交朋友操作 } }
12、Lambda表达式 案例(第十天第二部分)