java-内部类

Posted 8亩田

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java-内部类相关的知识,希望对你有一定的参考价值。

概要图

一 实现

当A类中的内容要被B类直接访问,而A类还需要创建B的对象,访问B的内容时,
这时,可以将B类定义到A类的内部。这样访问更为便捷。

将B称之为内部类(内置类,嵌套类)。

1.1 访问方式

内部类可以直接访问外部类中的所有成员,包含私有的。
而外部类要想访问内部类中的成员,必须创建内部类的对象。

当描述事物时,事物的内部还有事物,这个内部的事物还在访问外部事物中的内容。
这时就将这个事物通过内部类来描述。

例:孙悟空找铁扇公主借扇子,孙悟空就是个内部类

1.2 内部类被访问的方式

情况一:内部类在成员位置上的被访问方式。
成员是可以被指定的修饰符所修饰的。

访问方式:

1 内部类直接可以访问外部类

2 外部类访问内部类 [Out.]Inner in =new [Out.] Inner(); 

3 其他类访问内部类 Out.Inner in = new Out().new Inner();
public:不多见:因为更多的时候,内部类已经被封装到了外部类中,不直接对外提供。
static: 

4 在其他类中,对静态内部的非静态成员进行调用  Out Inner in = new Out.Inner();

5  静态内部类有静态成员  Outer.Inner.staticShow();

class Outer//外部类。
{
private static int num = 4;
public class Inner//内部类。
{
void show()
{
System.out.println("num="+num);
}
//    static void show1(){}//非静态内部类中不允许定义静态成员。仅允许在非静态内部类中定义 静态常量 static final。
//    如果想要在内部类中定义静态成员,必须内部类也要被静态修饰。
}

/*
内部类被静态修饰后,随着Outer的加载而加载。可以把一个静态内部类理解为就是一个外部类。

*/
static class Inner2
{
void show2()
{
System.out.println("Inner2 show2 run..."+num);
}
static void staticShow()
{
System.out.println("Inner2 staticShow run");
}
}
void method()
{
/*Outer.*/Inner in = new /*Outer.*/Inner();
in.show();
}
}

 
class InnerClassDemo
{
public static void main(String[] args)
{
//    Outer out = new Outer();
//    out.method();

//测试情况一:直接访问Outer中的Inner内部类的非静态成员。
//创建内部类的对象就哦了。内部类作为成员,应该先有外部类对象,再有内部类对象。
//    Outer.Inner in = new Outer().new Inner();
//    in.show();

//测试情况二:对静态内部类中的非静态成员进行调用。
//因为内部类是静态,所以不需要创建Outer的对象。直接创建内部类对象就哦了。
//    Outer.Inner2 in = new Outer.Inner2();
//    in.show2();
//    如果静态内部类有静态成员,该如何访问呢?既然静态内部类已随外部类加载,而且静态成员随着类的加载而加载,
//    就不需要对象,直接用类名调用即可。
//    Outer.Inner2.staticShow();
}
}

 1.3 总结访问方式

测试情况一:直接访问Outer中的Inner内部类的非静态成员。
创建内部类的对象就哦了。内部类作为成员,应该先有外部类对象,再有内部类对象。
    Outer.Inner in = new Outer().new Inner();
   in.show();

测试情况二:对静态内部类中的非静态成员进行调用。
1 因为内部类是静态,所以不需要创建Outer的对象。直接创建内部类对象就哦了。
    Outer.Inner2 in = new Outer.Inner2();
   in.show2();
2 如果静态内部类有静态成员,该如何访问呢?既然静态内部类已随外部类加载,而且静态成员随着类的加载而加载,
   就不需要对象,直接用类名调用即可。
   Outer.Inner2.staticShow();

二 特性

2.1 内部类作为类型的特性

  1.  内部类型不能和外层类型同名
    The nested type Out cannot hide an enclosing type
  2. 内部类中可以声明成员变量和成员方法,内部类可以与外部类同名,内部接口中可以声明成员变量和成员方法

  3. 内部类可以继承父类或实现接口

2.2 内部类作为成员的特性

  1. 使用点运算符"."引用内嵌类型,语法格式如下
    外层类型.内嵌类型
  2. 作为成员内嵌类型和其外层类型彼此信任.能访问对方的所有成员

  3. 内部接口总是静态的,内部类可声明是静态的或实例的,静态内部类能够声明静态成员但不能引用外部类的实例成员
    实例成员不能声明静态成员
    静态内部类能够声明静态成员但不能引用外部类的实例成员

    实例成员不能声明静态成员

  4. 在实例内部类中,使用以下语法引用或调用外部类当前实例的成员变量或实例成员方法

    外部类.this.成员变量   // 引用外部类当前实例的成员变量
    外部类.this.实例成员方法([参数列表])  // 使用外部类当前实例的成员方法

 

 

三 内部类访问外部类的原因

为什么内部类就能直接访问外部类中的成员呢?
那是因为内部类其实持有了外部类的引用 外部类.this
对于静态内部类不持有 外部类.this 而是直接使用 外部类名。

class Outer
{
int num = 3;
class Inner
{
int num = 4;

void show()
{
int num = 5;
System.out.println("num="+num);
System.out.println("num="+this.num);
System.out.println("num="+Outer.this.num);
}
}

void method()
{
//    System.out.println(num);
new Inner().show();
}
}


class InnerClassDemo2 
{
public static void main(String[] args) 
{
Outer out = new Outer();
out.method();
}
}

结果:

num=5
num=4
num=3


 

四 局部内部类的特点



内部类其实也可以定义在外部类的局部位置上。

内部类定义在局部时,只能访问被final修饰的局部变量
为啥呢?因为编译生产的class中直接操作那个最终数值了。

为什么不能访问非最终的局部变量呢?@@@@

class Outer
{
int num = 3;
void method()
{
final int x = 10;
//    final int x = 5;//局部变量。
int y = 2;
class Inner//局部内部类。不能被成员修饰符修饰。
{
void show()
{
//    System.out.println("y="+y);//访问失败。y的生命周期太短了。
System.out.println("x="+x);
System.out.println("inner show run........"+num);
}
}
new Inner().show();
}
}

 

/*
class Outer2
{
Object obj;
public void method()
{
int y = 9;
class Inner //extends Object
{
//覆盖了toString方法。
public String toString()
{
return "tostring:"+y;//假设可以访问y。
}
}
obj = new Inner();//给obj复制一个Inner对象。
}

public void function()
{
System.out.println(obj.toString());
}
}
*/
class InnerClassDemo3 
{
public static void main(String[] args) 
{
Outer out = new Outer();
out.method();
}
}

 五 内部类的继承和实现

看API,发现类名或者接口名称中有 . 说明是内部类,或者内部接口。

内部类的延伸。
内部类是可以继承或者实现外部其他的类或者接口的。

好处:通过内部类的方式对类进行继承重写,或者接口进行实现。
通过公共的方式对其内部类对象进行访问。因为通常内部类很有可能被外部类封装其中。
我们就可以通过父类或者接口的方式访问到内部类对象。

 

abstract class AbsDemo
{
abstract void show();
}

class Outer
{
int num = 3;
// 1 继承
private class Inner extends AbsDemo { //重写抽象方法show。 void show() { System.out.println("num="+num); } } //获取内部类的对象。 public AbsDemo getObject() { return new Inner(); }
public Inner getObject2()
{
return new Inner();
}
public void method() { new Inner().show(); } } class InnerClassDemo4 { public static void main(String[] args) { Outer out = new Outer(); // out.method(); //如果Inner对外提供,可以如此获取。 // Outer.Inner in = out.getObject2(); // in.show(); //如果Inner被private 。可以通过父类型获取。 AbsDemo a = out.getObject();//多态。 a.show(); } }

 六 匿名内部类

6.1 匿名内部类的认识

对以下代码进行简化。
匿名内部类:其实就是一个带有内容的子类对象。
格式:new 父类or接口(){子类的内容}
匿名内部类就是内部类的简化形式。
别忘了:匿名内部类有前提,内部类必须要继承父类或者实现接口。

abstract class AbsDemo
{
abstract void show();
}


class Outer
{
int num = 3;

public void method()
{


/*
不想创建具体的子类型。还想创建AbsDemo的子类对象。
怎么实现呢?没有子类型干脆,直接使用父类型就哦了。
可是在该例子中是抽象类,怎么可以new对象呢?
抽象类之所以不能new对象是因为抽象方法没重写。直接重写不就哦了吗?
*/
new AbsDemo()//这就是传说中的一个AbsDemo的子类对象。只不过这个对象有点胖!这是一个带着内容的子类对象。
//这种写法一个称呼:匿名内部类。
{
//重写抽象的show方法。
void show()
{
System.out.println("num===="+num);
}
}.show();

}
}

class InnerClassDemo5 
{
public static void main(String[] args) 
{
Outer out = new Outer();
out.method();
}
}

 6.2 匿名内部类的使用

interface Inter
{
    void show1();
    void show2();
}

class Outer
{
    int num = 4;
    
    //在一个类使用一个接口的对象。可以通过内部类来实现。
        public void method()
    {
       下面是实现
 //对其简化,写成匿名内部类的方式。
        Inter in = new Inter()//记住:内部类中一般方法不要过多。阅读性会很差。
        {
            public void show1()
            {}
            public void show2()
            {}
        };
        in.show1();
        in.show2();

 


        
    }
}

 

 6.3 匿名类练习

  题目1

问题

//匿名内部类练习。
interface Inter
{
    public void show();
}
class Outer
{
    //代码补足。要求使用匿名内部类。
    
    

}

class InnerClassDemo7 
{
    public static void main(String[] args) 
    {
        Outer.method().show();
        
    }
}

 

 

答案

//匿名内部类练习。
interface Inter
{
    public void show();
}
class Outer
{
    //代码补足。要求使用匿名内部类。
    public static Inter method()
    {
1
//既然在Oute类中使用到了Inter的对象。可以使用内部类来完成。 //需要子类型,只要简化格式即可,因为接口中就只有一个方法。 return new Inter() { public void show() { //code..; } }; // return new Inner(); } /*
2
// 还原成内部类。 当静态方法访问内部类时,内部类必须是静态的。 static class Inner implements Inter { public void show(){} } */ }class InnerClassDemo7 { public static void main(String[] args) { Outer.method().show(); /* Outer.method():Outer类中有一个method的方法。这个方式静态的。 Outer.method.show():能调用show()的必然是对象,说明method方法运算完应该返回一个对象。而且能调用Inter中的show方法,说明这个对象的类型是Inter。 */ Inter in = Outer.method();new InterImpl(); in.show(); } }

 

 注意

//Outer$Inner.class 成员内部类Inner
//Outer$1Inner.class 局部内部类名称为Inner
//Outer$2.class//匿名内部类。

题目 2

问题见下面红色字体

class Outer2
{
    public void method()
    {
        //以下两个对象有区别吗?
1 new Object() { public void show(){} }.show();//这个可以编译通过。 2 Object obj = new Object() { public void show(){} }; // obj.show();//编译失败。为啥呢?因为匿名内部类是子类对象,当Object obj指向时,就被提升了Object。而Object类中没有定义show方法,编译失败。 } }

 七 习题

1,代码补足。
interface Inter
{
    void show(int a,int b);
    void func();
}
class Demo
{
    public static void main(String[] args)
    {
        //补足代码;调用两个函数,要求用匿名内部类
        
            
        Inter in = new Inter()
        {
            public void show(int a,int b)
            {
                System.out.println(a+b);
            }
            public void func(){}
        };
        in.show(3,4);
        in.func();

    }
}



2,哪个答案是正确的,为什么?把每一个选项的正确错误的原因写上。
class Demo
{    
    public void func()
    {
        //位置1;
        this.new Inner();
        
    }
    class Inner{}//内部类。
    public static void main(String[] args)
    {
        Demo d=new Demo();
        // 位置2 
//        d.new Inner();
        new d.Inner();

        new Demo.Inner()
    }
}

A.在位置1写 new Inner();//可以的,外部类访问内部类需要创建对象。
B.在位置2写 new Inner();//错误。d.new Inner();
C.在位置2写 new d.Inner();//格式。    new new Demo().Inner();
D.在位置2写 new Demo.Inner();//不行,Inner必须是静态的。



3,补足代码。

interface Test
{
    void func();
}
class Demo
{
    public static void main(String[] args)
    {
        //通过主函数调用show,补足代码;通过(匿名内部类)进行show方法参数传递。

        new Demo().show(new Test()
        {
            public void func(){}
        });
        
        
    }
    void show(Test t)
    {
        t.func();
    }
}

 

以上是关于java-内部类的主要内容,如果未能解决你的问题,请参考以下文章

# Java 常用代码片段

# Java 常用代码片段

elasticsearch代码片段,及工具类SearchEsUtil.java

片段 - 全局视图变量与本地和内部类侦听器和内存泄漏

为啥片段类应该是公开的?

ForegroundService没有从片段开始?