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

浙公网安备 33010602011771号