Java内部类和匿名内部类的用法说明

 更新时间:2020年08月17日 11:12:11   作者:pan_jinquan  
这篇文章主要介绍了Java内部类和匿名内部类的用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

一、内部类: 

(1)内部类的同名方法

内部类可以调用外部类的方法,如果内部类有同名方法必须使用"OuterClass.this.MethodName()"格式调用(其中OuterClass与MethodName换成实际外部类名及其方法;this为关键字,表示对外部类的引用);若内部类无同名方法可以直接调用外部类的方法。

但外围类无法直接调用内部类的private方法,外部类同样无法直接调用其它类的private方法。注意:内部类直接使用外部类的方法与该方法的权限与是否static无关,它取决于内部类是否有同名方法。

package innerclass;
public class OuterClass {
 private void outerMethod() {
 System.out.println("It's Method of OuterClass");
 }
 public static void main(String[] args) {
 OuterClass t = new OuterClass();
 OuterClass.Innerclass in = t.new Innerclass();
 in.innerMethod();
 }
 
 class Innerclass {
 public void innerMethod() {
   OuterClass.this.outerMethod();// 内部类成员方法与外部类成员方法同名时,使用this调用外部类的方法
   outerMethod();// 内部类没有同名方法时执行外部类的方法
 }
 private void outerMethod() {
  System.out.println("It's Method of Innerclass");
 }
 }
}

输出结果为:

It's Method of OuterClass

It's Method of Innerclass

(2)内部类访问外部类的变量必须声明为final

方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。

首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。

注意,若使用JDK1.8,方法中内部类的方法是可以直接访问外部类的方法的局部变量,并且不需要声明为final类型。

public class OuterClass {
 int num1 = 0;// 成员变量
 
 private void outerMethod() {
 int num2 = 0;// 方法内的局部变量
 class Innerclass_1 {
  public void innerMethod() {
  System.out.println(num1);// 方法中内部类的方法,可以正常访问外部类的成员变量
  System.out.println(num2);// JDK1.8以前,方法中内部类的方法,不能直接访问外部类的方法的局部变量,必须声明为final
  }
 }
 }
}

如果使用JDK1.8以前的版本,Eclipse会出现如下错误提示:

(3)内部类的实例化

内部类实例化不同于普通类,普通类可以在任意需要的时候实例化,而内部类必须在外层类实例化以后方可实例化,并与外部类建立关系

因此在外部类中的非static方法中,是可以实例化内部类对象

private void outerMethod() {
 System.out.println("It's Method of OuterClass");
 Innerclass in = new Innerclass();//在外部类的outerMethod方法中实例化内部类是可以啊
 }

但在static方法中,就要注意啦!!!!不能在static方法中直接new内部类,否则出现错误:

No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass).

这是因为静态方法是在类实例化之前就可以使用的,通过类名调用,这时动态内部类都还没实例化呢,怎么用,总不能调用一个不存在的东西吧。

如果想在Static方法中new内部类,可以把内部类声明为Static

public class OuterClass {
 private void outerMethod() {
 System.out.println("It's Method of OuterClass");
 }
 
 public static void main(String[] args) {
 Innerclass in = new Innerclass();
 in.innerMethod();
 }
 
 static class Innerclass {//把内部类声明为static
 public void innerMethod() {
  System.out.println("It's Method of innerMethod");
 
 }
 }
 
}

当然,一般不使用static的方式,而是推荐这种方法:x.new A() ,其中 x是外部类OuterClass的实例,A是内部类Innerclass

package innerclass;
public class OuterClass {
 private void outerMethod() {
 System.out.println("It's Method of OuterClass");
 }
 public static void main(String[] args) {
 OuterClass.Innerclass in = new OuterClass().new Innerclass();//使用x.new A()的方式
 in.innerMethod();
 }
 class Innerclass {
 public void innerMethod() {
  System.out.println("It's Method of innerMethod");
 }
 }
}

x.new A() ,其中 x是外部类OuterClass的实例,A是类部类Innerclass,当然可以拆分如下,这样就显然很明白啦:

public static void main(String[] args) {
 OuterClass out = new OuterClass();//外部实例
 OuterClass.Innerclass in = out.new Innerclass();//外部实例.new 外部类
 in.innerMethod();
 }

(4)什么情况下使用内部类

典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外层类的对象。所以你可以认为内部类提供了某种进入其外层类的窗口。

使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外层类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

(5)在静态方法中实例化内部类例子:(内部类放在静态方法中)

package javatest2;
public class JavaTest2 {
 public static void main(String[] args) {
 class Boy implements Person {
  public void say() {// 匿名内部类自定义的方法say
  System.out.println("say方法调用");
  }
  @Override
  public void speak() {// 实现接口的的方法speak
  System.out.println("speak方法调用");
  }
 }
 Person per = new Boy();
 per.speak();// 可调用
 per.say();// 不能调用
 }
}
interface Person {
 public void speak();
}

per.speak()可调用,而per.say()不能调用,这时因为per是Person对象,要想调用子类的方法,可以强制向下转型为:((Boy) per).say();或者直接改为Boy per = new Boy();。从中可发现,要想调用内部类的自定义的方法,必须通过内部类的对象来调用。那么,匿名内部类连名字都没有,怎么调用内部类自定义的方法?

(二)匿名内部类

匿名内部类也就是没有名字的内部类正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。

关于匿名内部类还有如下两条规则:

1)匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类定义成抽象类。

2)匿名内部类不等定义构造器(构造方法),因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块,

怎样判断一个匿名类的存在啊?看不见名字,感觉只是父类new出一个对象而已,没有匿名类的名字。

先看段伪代码

abstract class Father(){
....
}
public class Test{
  Father f1 = new Father(){ .... } //这里就是有个匿名内部类
}

一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。

其实拆分上面的匿名内部类可为:

class SonOne extends Father{
 ...    //这里的代码和上面匿名内部类,大括号中的代码是一样的
}
public class Test{
  Father f1 = new SonOne() ;
}

先看一个例子,体会一下匿名内部类的用法:

运行结果:eat something

可以看到,我们直接将抽象类Person中的方法在大括号中实现了,这样便可以省略一个类的书写。并且,匿名内部类还能用于接口上

public class JavaTest2 {
 public static void main(String[] args) {
 Person per = new Person() {
  public void say() {// 匿名内部类自定义的方法say
  System.out.println("say方法调用");
  }
  @Override
  public void speak() {// 实现接口的的方法speak
  System.out.println("speak方法调用");
  }
 };
 per.speak();// 可调用
 per.say();// 出错,不能调用
 }
}
 
interface Person {
 public void speak();
}

这里per.speak()是可以正常调用的,但per.say()不能调用,为什么呢?注意Person per = new Person()创建的是Person的对象,而非匿名内部类的对象。其实匿名内部类连名字都没有,你咋实例对象去调用它的方法呢?但继承父类的方法和实现的方法是可以正常调用的,本例子中,匿名内部类实现了接口Person的speak方法,因此可以借助Person的对象去调用。

若你确实想调用匿名内部类的自定义的方法say(),当然也有方法:

(1)类似于speak方法的使用,先在Person接口中声明say()方法,再在匿名内部类中覆写此方法。

(2)其实匿名内部类中隐含一个匿名对象,通过该方法可以直接调用say()和speak()方法;代码修改如下:

public class JavaTest2 {
 public static void main(String[] args) {
 new Person() {
  public void say() {// 匿名内部类自定义的方法say
  System.out.println("say方法调用");
  }
 
  @Override
  public void speak() {// 实现接口的的方法speak
  System.out.println("speak方法调用");
  }
 }.say();// 直接调用匿名内部类的方法
 }
}
interface Person {
 public void speak();
}

补充知识: Java内部类的定义、如何创建内部类、内部类的分类、内部类与外部类的关系

1. 内部类的基本概念

1.1 内部类的定义

内部类: 所谓内部类就是在一个类内部进行其他类结构的嵌套操作。

class Outer{
  private String str ="外部类中的字符串";
  //************************** 
  //定义一个内部类
  class Inner{
    private String inStr= "内部类中的字符串";
    //定义一个普通方法
    public void print(){
      //调用外部类的str属性
      System.out.println(str);
    }
  }
  //************************** 
  //在外部类中定义一个方法,该方法负责产生内部类对象并调用print()方法
  public void fun(){
    //内部类对象
    Inner in = new Inner();
    //内部类对象提供的print
    in.print();
  }
}
public class Test{
  public static void main(String[] args)
  {
    //创建外部类对象
    Outer out = new Outer();
    //外部类方法
    out.fun();
  }
}

运行结果:外部类中的字符串

但是如果去掉内部类:

class Outer{
  private String outStr ="Outer中的字符串";
  public String getOutStr()
  {
    return outStr;
  }
  public void fun(){ //2
    //this表示当前对象
    Inner in = new Inner(this); //3
    in.print();         //5
  }
}
class Inner{
  private String inStr= "Inner中的字符串";
  private Outer out;
  //构造注入
  public Inner(Outer out) //3
  {
    this.out=out;    //4.为Inner中的out变量初始化
  }
  public void print(){  //6
    System.out.println(out.getOutStr()); //7
  }
} 
public class Test{
  public static void main(String[] args)
  {
    Outer out = new Outer(); //1.
    out.fun(); //2.
  }
}

执行结果:Outer中的字符串

但是去掉内部类之后发现程序更加难以理解。

1.2 内部类的优缺点

内部类的优点:

内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。

内部类是另外一种封装,对外部的其他类隐藏。

内部类可以实现java的单继承局限。

内部类的缺点:

结构复杂。

记录:使用内部类实现多继承:

class A {
  private String name = "A类的私有域";
  public String getName() {
    return name;
  }
}
class B {
  private int age = 20;
  public int getAge() {
    return age;
  }
}
class Outter {
  private class InnerClassA extends A {
    public String name() {
      return super.getName();
  }
}
  private class InnerClassB extends B {
    public int age() {
      return super.getAge();
  }
}
  public String name() {
    return new InnerClassA().name();
  }
  public int age() {
    return new InnerClassB().age();
  }
}
public class Test2 {
    public static void main(String[] args) {
      Outter outter = new Outter();
      System.out.println(outter.name());
      System.out.println(outter.age());
    }
}

2. 创建内部类

2.1 在外部类外部 创建非静态内部类

语法: 外部类.内部类 内部类对象 = new 外部类().new 内部类();

举例: Outer.Inner in = new Outer().new Inner();

2.2 在外部类外部 创建静态内部类

语法: 外部类.内部类 内部类对象 = new 外部类.内部类();

举例: Outer.Inner in = new Outer.Inner();

2.3 在外部类内部创建内部类语法

在外部类内部创建内部类,就像普通对象一样直接创建:Inner in = new Inner();

3. 内部类的分类

在Java中内部类主要分为成员内部类、静态内部类、方法内部类、匿名内部类

3.1 成员内部类

类比成员方法

成员内部类内部不允许存在任何static变量或方法 正如成员方法中不能有任何静态属性 (成员方法与对象相关、静态属性与类有关)

class Outer {
  private String name = "test";
  public static int age =20;

  class Inner{
    public static int num =10;
    public void fun()
    {
      System.out.println(name);
      System.out.println(age);
    }
  }
}
public class Test{
  public static void main(String [] args)
  {}
}

2. 成员内部类是依附外部类的,只有创建了外部类才能创建内部类。

3.2 静态内部类

关键字static可以修饰成员变量、方法、代码块、其实还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,静态内部类和非静态内部类之间存在一个最大的区别,非静态内部类在编译完成之后会隐含的保存着一个引用,该引用是指向创建它的外围类,但是静态类没有。没有这个引用就意味着:

1.静态内部类的创建不需要依赖外部类可以直接创建。

2.静态内部类不可以使用任何外部类的非static类(包括属性和方法),但可以存在自己的成员变量。

class Outer {
  public String name = "test";
  private static int age =20;

  static class Inner{
    private String name;
    public void fun()
    {
      System.out.println(name);
      System.out.println(age);
    }
  }
}
public class Test{
  public static void main(String [] args)
  {
    Outer.Inner in = new Outer.Inner();
  }
}

3.3 方法内部类

方法内部类顾名思义就是定义在方法里的类

1.方法内部类不允许使用访问权限修饰符(public、private、protected)均不允许。

class Outer{
  private int num =5;
  public void dispaly(final int temp)
  {
    //方法内部类即嵌套在方法里面
    public class Inner{
    }
  }
}
public class Test{
  public static void main(String[] args)
  {}
}

2. 方法内部类对外部完全隐藏,除了创建这个类的方法可以访问它以外,其他地方均不能访问 (换句话说其他方法或者类都不知道有这个类的存在)方法内部类对外部完全隐藏,出了创建这个类的方法可以访问它,其他地方均不能访问。

3. 方法内部类如果想要使用方法形参,该形参必须使用final声明(JDK8形参变为隐式final声明)

class Outer{
  private int num =5;
  //普通方法
  public void dispaly(int temp)
  {
    //方法内部类即嵌套在方法里面
    class Inner{
      public void fun()
      {
        System.out.println(num);
        temp++;
        System.out.println(temp);
      }
    }
    //方法内部类在方法里面创建
    new Inner().fun();
  }
}
public class Test{
  public static void main(String[] args)
  {
    Outer out = new Outer();
    out.dispaly(2);
  }
}

3.4 匿名内部类

匿名内部类就是一个没有名字的方法内部类,因此特点和方法与方法内部类完全一致,除此之外,还有自己的特点:

1.匿名内部类必须继承一个抽象类或者实现一个接口。

2.匿名内部类没有类名,因此没有构造方法。

//匿名内部类
//声明一个接口
interface MyInterface {
  //接口中方法没有方法体
  void test();
}
class Outer{
  private int num = 5;
  public void dispaly(int temp)
  {
    //匿名内部类,匿名的实现了MyInterface接口
    //隐藏的class声明
    new MyInterface()
    {
      public void test()
      {
        System.out.println("匿名实现MyInterface接口");
        System.out.println(temp);
      }
    }.test();
  }
}
public class Test{
  public static void main(String[] args)
  {
    Outer out = new Outer();
    out.dispaly(3);
  }
}

4. 内部类与外部类的关系

对于非静态的内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。内部类可以直接访问外部类的元素(包括私有域)—外部类在内部类之前创建,创建内部类时会将外部类的对象传入

class Outer{
  //成员变量 与对象有关
  private String msg;
  private int age;
  //--------------------------
  class Inner{
    public void dispaly()
    {
      //此处有一个隐藏的Outer.this
      msg = "test";
      age = 20;
      System.out.println(msg);
      System.out.println(age);
    }
  }
  //--------------------------
  public void test()
  {
    Inner in = new Inner();
    in.dispaly();
  }
}
public class Test{
  public static void main(String[] args)
  {
    Outer out = new Outer();
    out.test();
  }
}

外部类可以通过内部类的引用间接访问内部类元素 – -要想访问内部类属性,必须先创建内部类对象

class Outer{
  public void dispaly()
  {
    //外部类通过创建内部类的对象间接访问内部类元素
    Inner in = new Inner();
    in.dispaly();
  }
  class Inner{
    public void dispaly()
    {
      System.out.println("内部类");
    }
  }
}
public class Test1{
  public static void main(String[] args)
  {
    Outer out = new Outer();
    out.dispaly();
  }
}

内部类是一个相对独立的个体,与外部类没有关系。

以上这篇Java内部类和匿名内部类的用法说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • spring事务里面开启线程插入报错了是否会回滚

    spring事务里面开启线程插入报错了是否会回滚

    这篇文章主要介绍了spring事务里面开启线程插入,报错了是否会回滚?这是小编遇到一道面试题,题目大概是这个样子,今天抽空通过示例代码给大家分析下,需要的朋友可以参考下
    2023-04-04
  • MyBatis拦截器原理探究

    MyBatis拦截器原理探究

    MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.这篇文章主要介绍了MyBatis拦截器原理探究,需要的朋友可以参考下
    2018-02-02
  • IDEA的Project无法正常显示的问题解决

    IDEA的Project无法正常显示的问题解决

    本文主要介绍了IDEA的Project无法正常显示的问题解决,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • Java8接口的默认方法

    Java8接口的默认方法

    这篇文章主要为大家介绍了Java8接口的默认方法,还为大家默认方法的多重继承,感兴趣的朋友可以参考一下
    2016-01-01
  • 使用Java代码进行因数分解和求最小公倍数的示例

    使用Java代码进行因数分解和求最小公倍数的示例

    这篇文章主要介绍了使用Java代码进行因数分解和求最小公倍数的示例,都是基于最基础的算法原理实现,需要的朋友可以参考下
    2015-11-11
  • 解读为何java中的boolean类型是32位的

    解读为何java中的boolean类型是32位的

    这篇文章主要介绍了为何java中的boolean类型是32位的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Gson解析空字符串发生异常的处理方法

    Gson解析空字符串发生异常的处理方法

    最近在一个项目中遇到一个问题,当面对一些不规范的json,我们的gson解析经常会抛出各种异常导致app崩溃,通过在网上查找资料,找到了原因,这篇文章给大家介绍了一些可以采取的措施来避免这种情况,有需要的朋友们可以一起来学习学习。
    2016-11-11
  • java使用stream判断两个list元素的属性并输出方式

    java使用stream判断两个list元素的属性并输出方式

    这篇文章主要介绍了java使用stream判断两个list元素的属性并输出方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Spring Boot 2.x基础教程之使用@Scheduled实现定时任务的方法

    Spring Boot 2.x基础教程之使用@Scheduled实现定时任务的方法

    在Spring Boot中编写定时任务是非常简单的事,下面通过实例介绍如何在Spring Boot中创建定时任务,实现每过5秒输出一个当前时间,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • SpringCloudConfig之client端报错Could not resolve placeholder问题

    SpringCloudConfig之client端报错Could not resolve placeholder问

    这篇文章主要介绍了SpringCloudConfig之client端报错Could not resolve placeholder ‘from‘ in value “${from}“问题及解决方案,具有很好的参考价值,希望对大家有所帮助
    2022-12-12

最新评论