Java – 内部类
简介
在一个类的里面,再定义一个类。
本篇文章讲解Java中的各种内部类的定义、概念、使用场境等。
内部类
就是在类的内部再创建一个类
// 创建一个外部类
public class Car {
private String carName;
private int carAge;
// 创建一个内部类
class Engine{
public String engineName;
public int engineAge;
public void show(){
// 在内部类中可以访问自己的内部类成员属性
System.out.println(engineName);
// 在内部类中,可以访问外部类的私有成员属性
System.out.println(carName);
}
}
// 在外部类不能访问内部类的成员属性
System.out.println(engineName); // => 报错
}
要访问内部类的成员,需要实例化内部类,再访问。
内部类的分类
匿名内部类
概念:它是应用在类内部一种没有名字的类,这种内部类通常是用于实现接口或抽象类方法的,它的类本身没有名字,因此叫匿名内部类,详细解释可看下面讲解:
有名内部类的定义如下:
public class OutCalss {
public class InnerClass{
String name;
int age;
}
}
InnerClass 就是这个类的名字, 如果这个类需要实现某个接口,或需要重写某个抽象类中的方法
我们一般的操作是,通过创建一个新的java文件,然后定义一个新的类,让这个类去实现接口或抽象类中的抽象方法,如下代码所示
// 定义一个游泳的接口,要求实现类实现 swim() 方法
public interface Swim {
void swim();
}
如果我们需要实现Swim接口的方法,我们需要创建一个类文件,并实现Swim接口,如下代码
public class doSwim implements Swim{
@Override
public void swim() {
System.out.println("游泳的实现方法");
}
}
从上面的有名类实现Swim接口,对比匿名内部类与有名内部类的区别如下:
public class doSwim implements Swim{
@Override
public void swim() {
System.out.println("游泳的实现方法");
}
}
代码中的【public class doSwim implements Swim】定义我们可以看作是一个类的名称,和需要实现的接口声明,这部分可以看作是类的全名,而下面的粉色的代码可以看作是这个实现类的实现部分,那么我们可以这么看作,匿名类,就是没有类的全名,只有实现部分:
{
@Override
public void swim() {
System.out.println("游泳的实现方法");
}
}
但是如果只定义上面的代码,Java并不知道我们这段代码实现的是那个接口的方法,因此,接口名就要带在实现方法体之前作为说明:
Swim() {
@Override
public void swim() {
System.out.println("游泳的实现方法");
}
}
其中的Swim 指的是这个匿名类要实现的Swim接口而并非是匿名类的名称,() 指的是这个匿名类的构造函数在创建空参对象,而橙色的代码,才是这个匿名类的真实代码块
所以我们可以具体化一点,匿名类除了没有名称外,与一般的类是完全相同的,我们可以实现自己的方法,成员变量等,如下:
Swim(){
// 定义自己的成员变量
public String name;
public int age;
// 实现接口需要的方法
@Override
public void swim() {
}
// 定义自己的方法
public String getName(){
return this.name;
}
}
当我们需要创建一个类的对象时,我们使用以下方法创建:
Object o = new Object();
而创建匿名类时,也是同样,使用new 来创建即可:
new Swim(){
@Override
public void swim() {
System.out.println("内部类的实现接口方法");
}
};
因为它本身作为整个类,所以在创建对象时,需要使用【;】号进行结尾。
P.S:抽象类中的抽象方法,与接口一样:
// 创建一个抽象类,并创建一个抽象方法
public abstract class Swim {
public abstract void swim();
}
如果匿名类,需要实现抽象类中的方法,一样使用相同的套路创建就可以了,如下代码:
new Swim(){
@Override
public void swim() {
System.out.println("内部类的实现抽象方法");
}
};
此时只是对一个匿名类进行对象的创建,但没有对对象进行接收,我们可以通过多态方式进行接收:
Swim s = new Swim(){
@Override
public void swim() {
System.out.println("内部类的实现抽象方法");
}
};
格式:
new 抽象父类/接口 {
重写方法
@override
public void method(){}
};
匿名内部类的变化过程(*)
变换说明:
1.因为叫匿名内部类,所以定义类的名称就被删掉了,只剩下继承的 接口 或 抽象类。
去掉类名声明后只剩下类的函数体
{
@Override
public void show() {
}
}
2.因为要突出这个匿名类是继承那个抽象类或接口,所以要定义继承类
// 抽象类或接口 ( 构造参数列表 )
Inter ( 构造参数列表 ){
@Override
public void show() {
}
}
3.因为匿名类是没有名字的,所以就要先 new 出来,所以要在前面加上 new,和正常的 new Class(); 一样,后面都有一个 ";" 号,所以,我们 new 这个匿名内部类时都要在后面加上 ";"
// 抽象类或接口 ( 构造参数列表 )
new Inter ( 构造参数列表 ){
@Override
public void show() {
}
};
4.其它重写要求就和一般子类一样定义了,注意,抽象类和接口都一样定义。
P.S:虽然说匿名内部类,但实际上Java会在编译的时候自动帮我们加上这个类的类名,如Class$1.class
使用场景:
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象
如果实现类只要使用一次,就可以用匿名内部类简化代码
关于匿名内部类 {{}} 双大括号的概念
从上面的匿名内部类的学习我们知道,当使用匿名类时,实际上是创建一个没有名字的类代码块,它是依赖于实现接口,或实现抽象类中要实现的方法,对于接口,匿名类以"implements"方式实现,对于抽象类,匿名类以“extends”方式继承,那么可得,匿名类还可以像继承抽象类那样,继承普通类:
// 普通类,也能被匿名类进行继承
public class Swim {
public void swim(){
System.out.println("定义类的swim");
}
}
当我们使用匿名类时,则可以对普通类进行继承:
Swim s = new Swim(){};
注意:这里“Swim(){}”之后,所创建的就不是 Swim 类了,而是在Swim之下,创建了一个子类,并继承了 Swim 类,因此,“s”实际上是Swim的子类的对象,只是这个子类没有写任何代码;
根据Java特有的“代码块”特性,我们知道,每一个类都能提供一个代码块,如下:
public class Swim {
{
System.out.println("类的代码块");
}
public void swim(){
System.out.println("定义类的swim");
}
}
所以其实,Swim() {{}} 这种语法,实际上是匿名类继承了Swim类作为子类,并在创建这个子类时,调用代码块而已,即如下代码:
Swim s2 = new Swim(){{
System.out.println("创建一个子类继承Swim,并在代码块中执行");
}};
等同于
public class doSwim extends Swim{
{
System.out.println("创建一个子类继承Swim,并在代码块中执行");
}
}
因为匿名类实际上是使用了继承,又因为代码块是在对象创建时执行的,所以可以利用匿名类的代码块,来调用父类的方法,实现,如下应用实例中:
List<String> list = new ArrayList<String>(){
{
add("增加一个成员");
}
};
代码解析:利用匿名类,对ArrayList 以父类进行继承创建一个子类,在子类创建时,调用了匿名类的代码块,并调用父类的add方法。而对象"list"则不是ArrayList对象了,而是ArrayList的子类匿名类的对象了,因为匿名类使用了继承,所以 ArrayList<String> 中的泛型必须要填写。(好比你创建了一个新的子类来继承ArrayList,也必须在子类中定义泛型,不能省略了)。
成员内部类
1.写在成员位置的,属于外部类的成员。
public class Car {
class Engine{}
}
2. 成员内部类可以被一些修饰符所修饰,如 public , 不写(默认),protected , private , static 等,这些修饰符在内部类中和类成员一样作用。
3.在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
获取成员内部类对象
方法一:在外部类中编写方法,对外提供内部类的对象
方法二:直接创建格式 外部类类名.内部类类名 内部类实例 = new 外部类类名().new 内部类类名()
示例:Outer.Inner oi = new Outer().new Inner();
访问外部类成员
外部类成员变量和内部类成员变量重名时,在内部类如何访问?
public class Outer {
String name = "10";
class Inner {
String name = "20";
public void show(){
String name = "30";
// 我们需要如何才能获得这三个成员变量?
System.out.println(??); // => 10
System.out.println(??); // => 20
System.out.println(??); // => 30
}
}
}
1.通过就近原则,我们可以使用直接访问的方式取得离自己最近的成员变量值。
2.通过 this
方式获取本类的成员变量值。
3.通过 Outer.this
方式获取外部类的成员变量值
System.out.println(Outer.this.name); // => 10
System.out.println(this.name); // => 20
System.out.println(name); // => 30
静态内部类
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
public class Outer {
static class Inner {
public void show(){
}
}
}
创建静态内部类对象
外部类类名.内部类类名 内部类实例 = new 外部类类名.内部类类名()
public class Outer {
static class Inner {
public void show(){ }
public static void staticShow(){}
}
}
Outer.Inner oi = new Outer.Inner();
调用非静态方法的格式
先创建对象,用对象调用
public class Outer {
static class Inner {
public void show(){ }
public static void staticShow(){}
}
}
Outer.Inner oi = new Outer.Inner();
oi.show()
调用静态方法的格式
外部类名.内部类名.方法名();
public class Outer {
static class Inner {
public void show(){ }
public static void staticShow(){}
}
}
Outer.Inner.staticShow()
局部内部类
1.将内部类定义在方法里就叫做局部内部类,类似于方法里面的局部变量
2.外界是无法直接使用,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
public class Outer {
// 成员内部类
static class Inner {
public void show(){
// 成员方法中的局部内部类
class innerClass{
}
}
public static void staticShow(){
// 静态方法中的内部类
class innerStaticClass{
}
}
}
}
共有 0 条评论