Java基本语法之内部类示例详解

 更新时间:2022年03月18日 16:06:59   作者:未见花闻  
本文带大家认识Java基本语法——内部类,将一个类定义放在另一类的定义的内部,这个就是内部类,内部类允许将一些逻辑相关的类组织在一起,并能够控制位于内部的类的可视性,感兴趣的可以了解一下

1.内部类概念及分类

将一个类定义在另一个类的内部或者接口内部或者方法体内部,这个类就被称为内部类,我们不妨将内部类所在的类称为外围类,除了定义在类,接口,方法中的内部类,还有一种特殊的内部类,那就是使用关键字new创建一个匿名类的对象,而这个匿名类其实就是一个内部类,具体说是一个匿名内部类,经常用于传入构造器实参构造对象,例如PriorityQueue对象的创建。

一个类定义在另一个类的内部或者接口内部,并且没有static修饰时,这个内部类就称为实例内部类,使用static修饰时,这个内部类被称为静态内部类(嵌套类),将一个类定义在代码块内部,特别是方法体内部,这个内部类被称为本地内部类或者局部内部类,还有一种就是上面所说的匿名内部类。

2.实例内部类

2.1实例内部类的创建

实例内部类的定义很简单,就直接定义在外围类的里面就可以了。问题是如何创建一个内部类对象,首先,需要明白内部类与外围类中的成员变量与方法是平起平坐的,都是属于外围类对象的(无static修饰),所以想要内部类对象先得有外围类对象,第一种创建内部类对象的方式就是使用一个方法返回一个内部类对象,并且这个内部类对象的类型为OutClassName.InnerClassName,即外围类名.内部类名。

public class Outer {

    class Inner1 {
        private String str;
        public Inner1(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
    }

    class Inner2 {
        private int val;
        public Inner2(int i) {
            this.val = i;
        }
        public int readVal() {
            return this.val;
        }
    }
    //创建内部类
    public Inner1 creatInner1(String s) {
        return new Inner1(s);
    }
    public Inner2 creatInner2(int i) {
        return new Inner2(i);
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner1 inner1 =  outer.creatInner1("Inner1");
        Outer.Inner2 inner2 = outer.creatInner2(2);

        System.out.println(inner1.readStr());
        System.out.println(inner2.readVal());
    }
}
//output:
Inner1
2

Process finished with exit code 0

2.2使用.this和.new

当然,创建一个内部类还有其他的方法,就是使用外围类的对象.new来创建一个内部类对象,语法为:

外部类对象.new 内部类构造方法;

public class Outer {

    class Inner1 {
        private String str;
        public Inner1(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
    }

    class Inner2 {
        private int val;
        public Inner2(int i) {
            this.val = i;
        }
        public int readVal() {
            return this.val;
        }
    }


    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner1 inner1 =  outer.new Inner1("Inner1");
        Outer.Inner2 inner2 = outer.new Inner2(2);

        System.out.println(inner1.readStr());
        System.out.println(inner2.readVal());
    }
}
//output:
Inner1
2

Process finished with exit code 0

内部类的特性不止如此,内部类还能与外围类链接,首先实例内部类对象的创建是依赖外围类对象的引用,实例内部类与外围类的成员变量地位一样,如果想要通过内部类对象访问外围类的成员变量与方法(特别是同名变量)。在内部类中,可以使用外围类名.this获取外围类对象,然后使用获得的这个外围类对象来使用外围类的变量与方法。

public class Outer {
    private String outStr;
    public Outer(String str) {
        this.outStr = str;
    }

    public void printOuter() {
        System.out.println(this.outStr);
    }

    class Inner {
        private String str;
        public Inner(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
        //使用.this获取父类对象引用
        public Outer outer() {
            return Outer.this;
        }
    }



    public static void main(String[] args) {
        Outer outer = new Outer("outerString");
        Outer.Inner inner = outer.new Inner("innerString");

        Outer out = inner.outer();
        out.printOuter();
    }
}
//output:
outerString

Process finished with exit code 0

其实内部类对象中有两个this,直接使用this.成员获取的是内部类对象自己的成员,而像上面使用外围类名.this.成员获取的是外围类的成员,也就是说在内部类中this指向内部类对象自己的引用,外部类名.this指向外围类对象的引用。

当出现内部类与外围类变量名相同时,这两个this就有很大的用处了。

public class Outer {
    public int a = 12;
    public int b = 16;
    public int c = 20;

    class Inner{
        public int a = 8;
        public int c = 48;
        public int d = 2;

        public void printVal() {
            System.out.println("外围类变量a=" + Outer.this.a);
            System.out.println("外围类变量b=" + Outer.this.b);
            System.out.println("外围类变量c=" + Outer.this.c);
            System.out.println("内部类变量a=" + this.a);
            System.out.println("内部类变量c=" + this.c);
            System.out.println("内部类变量d=" + this.d);
        }
    }

    public Inner creatInner() {
        return new Inner();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();

        Outer.Inner inner = outer.creatInner();
        inner.printVal();
    }
}
//output:
外围类变量a=12
外围类变量b=16
外围类变量c=20
内部类变量a=8
内部类变量c=48
内部类变量d=2

Process finished with exit code 0

如果没有出现同名,直接获取的成员即可,可以不用使用上面所说的this,出现同名,直接访问的成员默认是内部类对象的。

2.3内部类实现迭代打印

内部类可以与外围类的所有元素的访问权限,所以我们可以尝试使用内部类来遍历输出外围类中的元素,虽然内部类与外围类的成员地位是平等,但内部类毕竟也是类,它也可以像外围类一样继承类和实现接口。

import java.util.Arrays;

interface Selector{
    //判断是否迭代完全部元素
    public boolean end();
    //获取当前元素
    public Object current();
    //迭代下一个元素
    public void next();
}

public class SequeArray {
    private Object[] items;
    private int usedSize;

    public SequeArray(int capacity) {
        items = new Object[capacity];
    }
    public void add(Object val) {
        if (usedSize == items.length) items = Arrays.copyOf(items, 2 * usedSize);

        items[usedSize++] = val;
    }

    private class SequeSelector implements Selector{
        private int index;

        public boolean end() {
            return index == usedSize;
        }
        public Object current() {
            return items[index];
        }
        public void next() {
            if (index < usedSize) index++;
        }
    }
    public SequeSelector sequeSelector() {
        return new SequeSelector();
    }

    public static void main(String[] args) {
        SequeArray array = new SequeArray(10);

        for (int i = 0; i < 10; i++) {
            array.add(i+1);
        }
        //发生了向上转型
        Selector selector = array.sequeSelector();

        while (!selector.end()) {
            System.out.print(selector.current() + "   ");
            selector.next();
        }
    }
}
//output:
1   2   3   4   5   6   7   8   9   10   
Process finished with exit code 0

2.4内部类的继承

内部类的继承有一点麻烦,因为内部类的构造器必须依赖外围类的引用,所以需要一段特殊的语法来说明内部类与外围类的关联:

外围类引用.super();

具体怎么使用看如下一段代码:

public class Outer {
    class Inner{
        
    }
}
class Person extends Outer.Inner {
    private String str;
    private int id;
    public Person(Outer out, String str, int id) {
        out.super();
        this.str = str;
        this.id = id;
    }
    public void readVal() {
        System.out.println(this.str + this.id);
        
    }
}

当内部类被继承的时候,需要通过外围类的引用传入父类的构造方法中并调用super()才能通过编译。

如果内部类继承外部类,直接继承即可,不需要这么麻烦。

程序生成一个java文件的字节码文件时,命名为公共外部类$内部类。

内部类可以无限嵌套,一般没人这么干。

3.静态内部类

静态内部类也称嵌套类,它就是在实例内部类的定义前加入一个static关键字,像下面这样:

public class Outer {
    
    static class Inner{
        
    }
}

与实例内部类的区别是静态内部类是属于类的而不是属于对象的,因此静态内部类的创建不依赖与外部类对象,这是和实例内部类最大的区别之一。

public class Outer {
    static class Inner{
        private String str;
        public Inner(String s) {
            str = s;
        }
    }

    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner("我是静态内部类!");
        System.out.println(inner.str);
    }
}
//output:
我是静态内部类!

Process finished with exit code 0

4.匿名内部类

匿名内部类的用法我们已经见过了,就是创建PriorityQueue对象时,默认创建的是小堆,需要传入比较器来创建大堆。

        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

像上面这种在方法中使用new关键字创建一个匿名类的对象作为实参,里面这个匿名类就是匿名内部类,创建过程中的Comparator匿名类对象是继承所需传参类型的,在这里也就是Comparator类。

如果使用自定义类型传入优先队列,是一定要实现比较器的,实现比较器最常见的方式就是匿名内部类与Lambda表达式。

假设我有一个Person类数组,需要对他们的name排序,如果不使用内部类,就需要在Person类中实现比较器。

import java.util.Arrays;
import java.util.Comparator;

class Preson {
    public String name;
    public Integer id;
    public Preson(String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public String toString() {
        return "Preson{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}
class PersonComparator implements Comparator<Preson> {
    @Override
    public int compare(Preson o1, Preson o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class Main {
    public static void main(String[] args) {
        Preson preson1 = new Preson("aboluo", 24);
        Preson preson2 = new Preson("zhousi", 25);
        Preson preson3 = new Preson("syyfjy", 2);
        Preson[] presons = {preson1, preson2, preson3};
        Arrays.parallelSort(presons, new PersonComparator());
        System.out.println(Arrays.toString(presons));
    }
}
//output:
[Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}]

Process finished with exit code 0

使用内部类上述代码就变为:

import java.util.Arrays;
import java.util.Comparator;

class Preson {
    public String name;
    public Integer id;
    public Preson(String name, Integer id) {
        this.name = name;
        this.id = id;
    }


    @Override
    public String toString() {
        return "Preson{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

public class Main {
    public static void main(String[] args) {
        Preson preson1 = new Preson("aboluo", 24);
        Preson preson2 = new Preson("zhousi", 25);
        Preson preson3 = new Preson("syyfjy", 2);
        Preson[] presons = {preson1, preson2, preson3};
        Arrays.parallelSort(presons, new Comparator<Preson>() {
            @Override
            public int compare(Preson o1, Preson o2) {
                return o1.name.compareTo(o2.name);
            }
        });
        System.out.println(Arrays.toString(presons));
    }
}
//output:
[Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}]

Process finished with exit code 0

还有一个本地内部类,就是类定义在方法中,就不多说了,基本上不用,用法如下:

public class Outer {
    public void func() {
        class Inner{
            //
        }
    }
}

以上就是Java基本语法之内部类示例详解的详细内容,更多关于Java内部类的资料请关注脚本之家其它相关文章!

相关文章

  • java实现文件编码转换的方法

    java实现文件编码转换的方法

    这篇文章主要为大家详细介绍了java实现文件编码转换的方法,分享一个文件编码转换的工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • MyBatis实现数据库类型和Java类型的转换

    MyBatis实现数据库类型和Java类型的转换

    MyBatis 在处理数据库查询结果或传递参数时,需要将数据库类型与 Java 类型之间进行转换,本文就给大家介绍MyBatis如何实现数据库类型和 Java 类型的转换的,需要的朋友可以参考下
    2024-09-09
  • IntelliJ IDEA设置JVM运行参数的图文介绍

    IntelliJ IDEA设置JVM运行参数的图文介绍

    这篇文章主要介绍了IntelliJ IDEA设置JVM运行参数的方法,包括配置方式及优先级,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • java实现连连看游戏

    java实现连连看游戏

    这篇文章主要为大家详细介绍了java实现连连看游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • springboot启动时没有日志的原因分析

    springboot启动时没有日志的原因分析

    这篇文章主要介绍了springboot启动时没有日志的原因分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 如何使用IDEA的groovy脚本文件生成带JPA注解的实体类(图文详解)

    如何使用IDEA的groovy脚本文件生成带JPA注解的实体类(图文详解)

    这篇文章主要介绍了如何使用IDEA的groovy脚本文件生成带JPA注解的实体类,本文通过图文并茂实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

    一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

    这篇文章主要介绍了一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • SpringBoot添加富文本编辑器操作步骤

    SpringBoot添加富文本编辑器操作步骤

    富文本编辑器是一种能够编辑和展示富文本内容的工具或程序,与纯文本编辑器不同,富文本编辑器可以处理文本的格式、样式、布局等方面,使文本更加丰富多样,本文给大家介绍了SpringBoot添加富文本编辑器操作步骤,需要的朋友可以参考下
    2024-01-01
  • IDEA 一直scanning files to index的四种完美解决方法(VIP典藏版)

    IDEA 一直scanning files to index的四种完美解决方法(VIP典藏版)

    这篇文章主要介绍了IDEA 一直scanning files to index的四种完美解决方法(VIP典藏版),推荐第四种方法,第四种方法摸索研究后得出,亲测好用,需要的朋友参考下吧
    2023-10-10
  • Java8 使用流抽取List<T>集合中T的某个属性操作

    Java8 使用流抽取List<T>集合中T的某个属性操作

    这篇文章主要介绍了Java8 使用流抽取List<T>集合中T的某个属性操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02

最新评论