内部类

  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表达式  案例(第十天第二部分)

原文地址:https://www.cnblogs.com/liuwei6/p/6572262.html