一、什么叫内部类
1.定义
内部类就是在一个类的内部定义的类,包含内部类的类被称作外部类。
2.内部类种类
- 可按照定义的位置区分:方法里定义的内部类称为
局部内部类 ,在方法外的叫成员内部类 ; - 也可按照种类区分:非静态内部类(无
static 修饰)、静态内部类(有static 修饰)、匿名内部类。 - 大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类是一种与成员变量、方法相似的类成员,成员内部类分为两种(非静态内部类、静态内部类),局部内部类和匿名内部类则不是类成员。
- 下图为这几种内部类关系
3.使用格式
class 外部类{
class 内部类{}
}
编译后会产生两个类文件:外部类.class 和内部类.class 。
4.作用
内部的作用如下
- 内部类提供了更好的封装性,内部类可以只在外部类中有用
- 内部类也是外部类的成员,所以内部类可以直接访问外部类的私有成员
- 匿名内部类用来创建那些仅需要一次使用的类
5.内部类与外部类的区别
- 内部类需定义在外部内的内部
- 内部类可以使用
private 、protected 、static 进行修饰,外部类不可以 - 非静态内部类不能有静态成员
二、 非静态内部类
1.定义
非静态内部类是没有用static修饰的成员内部类,写在外部类方法体外,外部类里。
2.举例
class Outer {
private String name;
public class Inner {
void print() {
System.out.println("Inner");
System.out.println(name);
}
}
public void innerPrint() {
Inner inner = new Inner();
inner.print();
}
}
public class TestInner {
public static void main(String[] args) {
Outer outer = new Outer();
outer.innerPrint();
}
}
上述的代码中定义了一个内部类Inner,外部类Outer里面包含了一个innerPrint方法,在该方法中创建了一个Inner对象,同时调用该对象的print方法。可以看到,在外部类中使用非静态内部类时与使用普通类没有太大区别。
3.内部类访问外部内成员变量需要注意
- 当在非静态内部类的方法内访问某个变量时,若有同名的变量,系统查找的优先级为:非静态内部类的该方法的局部变量>非静态内部类的成员变量>内部类所在的外部类
- 当在非静态内部类的方法内访问某个变量时,如果外部类成员变量、内部类成员变量、内部类方法里的局部变量同名,则可以通过
this 、外部类类名.this 来进行区分 - 非静态内部类的成员只在该非静态内部类内有效,不能被外部类直接使用。如果外部类要访问非静态内部类的成员,则必须显式的创建非静态内部类对象来访问其成员
- 外部类的静态成员不能直接使用非静态内部类
- 非静态内部类中不能定义静态成员,即非静态内部类中不能有static修饰的变量、方法、初始化代码块
如下:
class Outer {
private static String a = "a";
private String b = "b";
public class Inner {
static String c = "c";
private String d = "d";
void print() {
System.out.println(a);
System.out.println(b);
System.out.println(d);
System.out.println("Inner");
}
}
void innerPrint() {
Inner inner = new Inner();
inner.print();
}
}
三、静态内部类
1.定义
静态内部类是static 修饰的成员内部类,写在外部类方法体外,外部类里,相当于外部类的其他静态成员,没有外部类对象也可以直接访问静态内部类。
2.举例
静态内部类可以包含静态成员和非静态成员,静态内部类不能访问外部类的实例成员,只能够访问外部类的静态成员。
class OutClass {
private int a = 6;
private static int b = 7;
static class InClass {
private static int c = 8;
private int d = 9;
void show() {
}
static void show2() {
}
}
}
3.使用规则如下
- 静态内部类的非静态方法能访问外部类静态成员,静态内部类的非静态方法不能直接访问外部类实例成员,需要通过外部类对象访问外部实例成员
- 静态内部类的非静态方法能访问内部类静态成员和内部类的实例成员
- 静态内部类的静态方法能访问外部类静态成员,静态内部类的静态方法不能直接访问外部类的实例成员,需要通过外部类对象访问外部实例成员
- 静态内部类的静态方法能访问内部类静态成员,不能直接访问内部类的实例成员,需要通过内部类对象访问内部类的实例成员
- 外部类方法可通过内部类直接调用内部类静态方法,不可通过内部类直接调用内部类非静态方法,需要通过内部类对象调用内部类非静态方法
class OutClass {
private int a = 6;
private static int b = 7;
static class InClass {
private static int c = 8;
private int d = 9;
void show() {
System.out.println(a);
OutClass outClass = new OutClass();
System.out.println(outClass.a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
static void show2() {
System.out.println(a);
OutClass outClass = new OutClass();
System.out.println(outClass.a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
InClass inClass = new InClass();
System.out.println(inClass.d);
}
}
void A() {
InClass.show();
InClass.show2();
InClass inClass = new InClass();
inClass.show();
System.out.println(InClass.c);
System.out.println(inClass.d);
}
}
四、匿名内部类
先举个例子:
abstract class Fruit{
public abstract void eat();
}
class Apple extends Fruit{
@Override
public void eat() {
System.out.println("吃苹果补充维生素");
}
}
public class Example5_3 {
public static void main(String[] args) {
Fruit fruit = new Apple();
fruit.eat();
}
}
Apple继承Fruit类,实现了eat方法,然后创建Apple子类的一个实例,将其向上转型为Fruit类,然后创建了Apple子类的一个实例,将其向上转型为Fruit类的引用。但是,如果此处的Apple类只使用一次,那么将其编写为一个独立的类会麻烦很多,此时就可以引入匿名内部类来简化代码的编写。
匿名内部类就是一个没有显式定义名字的内部类。
创建匿名内部类时会立即创建一个该类的实例,匿名内部类适合创建只需要使用一次的类,这个类定义后立即消失。
1.格式
new 接口名()/父类构造器([参数列表]){
...
}
2.种类
从上面的格式中可以看出,创建匿名内部可实现接口或引用普通类。
(1)继承式匿名内部类
abstract class Fruit {
public abstract void eat();
}
public class Example5_4 {
public void test() {
Fruit fruit = new Fruit() {
@Override
public void eat() {
System.out.println("吃苹果补充维生素");
}
};
fruit.eat();
}
public static void main(String[] args) {
Example5_4 example5_4 = new Example5_4();
example5_4.test();
}
}
(2)接口式匿名内部类
interface Flyable {
void fly();
}
public class Example5_5 {
public void test() {
Flyable flyable = new Flyable() {
@Override
public void fly() {
System.out.println("飞机能飞,实现了Flyable的接口");
}
};
flyable.fly();
}
public static void main(String[] args) {
Example5_5 example5_5 = new Example5_5();
example5_5.test();
}
}
(3)参数式匿名内部类
interface Flyable {
void fly();
}
public class Example5_6 {
public void test(Flyable flyable) {
flyable.fly();
}
public static void main(String[] args) {
Example5_6 example5_6 = new Example5_6();
example5_6.test(new Flyable() {
@Override
public void fly() {
System.out.println("天鹅会飞");
}
});
}
}
3.使用规则
- 匿名内部类不能有构造方法,只能有一个实例
- 匿名内部类不能定义任何静态成员
- 匿名内部类不能用
public 、protected 、 private 、 static 修饰 - 匿名内部类不能重复使用,且一定在new后面,隐式的继承一个类或实现一个接口
五、Lambda表达式
1.定义
匿名内部类允许随用随建。JDK8后引入Lambda表达式,能更简洁的传递代码,主要作用就是代替匿名内部类的繁琐语法。
2.Lambda表达式说明
- 该词来自学术界开发出来的一套用来计算λ的演算法
- Lambda表达式是一种没有声明名称的匿名方法,但有参数列表、主体、返回类型,还可能有可以抛出异常的列表
- Lambda表达式可以作为参数传递给方法或存储在变量中
3.Lambda表达式的使用语法
有两种语法格式
(parameters)->(expression)
或者
parameters)->{statements}
parameters :根据情况可以无参数或多个参数- ->:将参数列表与主体分开
expression /statements :Lambda主体,表达式或语句构成的代码块
λ表达式本质上是一个匿名方法。让我们来看下面这个例子:
public int add(int x, int y) {
return x + y;
}
转成λ表达式后是这个样子:
(int x, int y) -> x + y;
参数类型也可以省略,Java编译器会根据上下文推断出来:
(x, y) -> x + y;
或者
(x, y) -> { return x + y; }
4.Lambda表达式使用场景
- Lambda表达式在函数式接口上使用
- 函数式接口是指只定义一个抽象方法的接口,此接口可以包含多个默认、静态方法,但是只有一个抽象方法详细
举个例子
interface Flyable {
void fly();
}
public class Example5_5 {
public void test(Flyable flyable) {
flyable.fly();
}
public static void main(String[] args) {
Example5_5 example5_5 = new Example5_5();
Flyable flyable = () -> {
System.out.println("鸟会飞");
};
example5_5.test(flyable);
System.out.println("鸟会飞");});
}
}
5.Lambda表达式与匿名内部类区别
- 匿名内部类可以为抽象类或普通类创建实例,可为任何接口创建实例;Lambda表达式只能为函数式接口创建实例
- 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法,Lambda表达式不允许调用接口中定义的默认方法
|