Java基本语法之内部类示例详解
作者:未见花闻 发布时间:2023-02-06 14:05:32
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{
//
}
}
}
来源:https://weijianhuawen.blog.csdn.net/article/details/123086479


猜你喜欢
- 为了追求更好的用户体验,有时候我们需要一个类似心跳一样跳动着的控件来吸引用户的注意力,这是一个小小的优化需求,但是在 Flutter 里动画
- 泛型概述我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进
- 问题项目是springcloud项目,在maven install某一个项目时报错:程序包com.example.commons.appli
- 前言项目中时不时遇到查字典表等数据,只需要返回数据,不需要写其他业务,每个字典表可能都需要写一个接口给前端调用,比较麻烦,所以采用下面这种方
- 目录原理实战原理其原理如图:1.配置数据源信息(包括表名)2.读取数据表字段信息:列名、类型、字段注释、表注释3.编写代码模板,并将该模板加
- nacos升级spring cloud 2020.0无法使用bootstrap.yml之前用spring cloud整合nacos,需要一个
- 什么是Handler?Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个H
- <?xml version="1.0" encoding="UTF-8"?><eh
- 获取map的值主要有四种方法,这四种方法又分为两类,一类是调用map.keySet()方法来获取key和value的值,另一类则是通过map
- 定义jdk8发布新特性中,lambda是一大亮点之一。lambda表达式能够简化我们对数据的操作,减少代码量,大大提升我们的开发效率。Lam
- 1. 包装类的介绍针对八种基本数据类型定义相应的引用类型--包装类(封装类),有了类的热点后,就可以调用类中的方法2. 基本数据类型 --&
- 前言:我们知道,在单体项目中,我们将用户信息存在 session 中,那么在该 session 过期之前,我们都可以从 session 中获
- 一、如何显示assets/license.txt(中文)的内容? (1)方法1:InputStream.available()得到字节数,然
- 本文实例为大家分享了Unity实现透视滑动列表的具体代码,供大家参考,具体内容如下1、目的有时候,为了实现更好的美术效果,需要实现一些特殊的
- 传统方式克隆羊问题现在有一只羊 tom,姓名为: tom,年龄为:1,颜色为:白色,请编写程序创建和 tom羊属性完全相同的10只羊。传统方
- 前言最近在做物联网课设,过程中需要用到Android的蓝牙API,奈何原生的蓝牙API使用有点麻烦。于是上网搜索看有没有好用的Android
- 在编写ui界面时因为手机分辨率大小不同,所以展现出来的效果也是不同的,这个时候就需要考虑适配器,让根据手机分辨率自动适配相应尺寸来展示界面,
- 首先写一个测试文件然后点击IDEA右侧的maven,然后选择package,之后点击上面运行或者直接双击即可,等下方控制台构建成功即可:然后
- 前言最近在给熔断器组件增加一个降级策略(Hystrix好像没有这个配置),我们提供了如下几种策略:1、默认策略2、返回常量值3、抛出指定异常
- 截取字符串最后一位1.用substring()来截取理论上它是按照字符串.substring(字符串.lastIndexOf("\