内部类(inner class)
内部类是定义在另一个类中的类,使用内部类可以达到很多数据访问上的需要
使用内部类的主要原因:
- 内部类可以对同一个包中的其他类隐藏
- 内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据
内部类原本对于简洁的实现回调很重要,现在有了更好的lambda表达式
and
##内部类具有一个指向外层的隐式引用##
- 在java中,只有内部类能被private修饰
- 内部类属于编译器现象,编译器会将内部类转换为一般的类文件,而虚拟机对此并不知情,它只知道可用的引用
- 一般内部类中所有静态字段有必须是final,并且初始化为一个编译时常量。不能有static方法 ,但是语言本身支持使用一个静态方法,但只能访问外围类的静态字段和方法,显然在这种情况下,对比过于复杂的情况,那点得到的好处有些得不偿失
——————————————————————————————————————
在方法中创建一个一次性的局部类
上例子:
public void start()
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone , the time is"
+Instant.ofEpochMilli(event.getwhen()));
if(beep)Toolkit.getDefaultToolkit().beep();
}
}
var listener = new TimePrinter();
var timer = new Timer(interval ,listener);
timer.start();
}
局部类的优点:对外界完全隐藏,仅对该start方法显示
★★案例2:通过外部方法获取参数访问局部变量!
局部变量要求是事实最终变量,即赋值以后不会再被改变(具有确定性或者被final修饰)
典型范例:
public void start(int interval.boolean beep)
{
class TimePrinter implements AcionListener
{
用到参数的值会以final类型复制进来
System.out.println("At the tone ,the time is"
+Instant. ofEpochMilli(event.getwhen()));
if(beep)Toolkit.getDefaultToolkit().beep();
}
}
var listener = new TimePrinter();
var timer = new Timer(interval,listener);
time.start();
———————————————————————————————————————
匿名内部类
使用内部类的时候,如果只是为了创建单个对象,甚至可以不用指定类的名字,这样创建出来的类就是匿名内部类
public void start(int interval,boolean beep)
{
var listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone ,the time is"
+Instant. ofEpochMilli(event.getwhen()));
if(beep)Toolkit.getDefaultToolkit().beep();
}
};
var timer = new Timer(interval,listener);
listener.start();
}
new 超类 (){类的定义}
var 变量 = new 类名 (){类的定义};
- 匿名内部类,匿去的是本类的名字,直接将本类implements接口或extends超类省略为直接用超类名字。如上例
- 内部类构造参数会传给父类构造器,只要内部类实现了一个接口,那么它就不能有任何构造参数————会默认引申为Object的子类
- 尽管匿名类不能有构造器,但是可以提供一个对象初始化块
- 一般作用:实现事件监听以及其他回调。但是如今最好还是使用lambda表达式
——————————————————————————————————————
相关思路 / 语法
- 双括号初始化
利用内部类语法,假如想创建一个数组列表,并且传输到一个方法:
var friends = new ArrayList< String>
friends.add("A");
friends.add("B");
invite(friend);
如果操作后不再需要这个列表,最好将其作为一个匿名列表
invite(new ArrayList< String>(){{add("A");add("B");}});
上述语句中,外层括号建立了ArrayList的一个匿名子类。内层则是一个初始化块
大多数情况下,可以使用List.of(“A”,“B”);代替
- 建立一个与超类大体类似的情况,对于匿名子类使用equals的时候要思考是否能够完成测试
- 使用静态方法的时候,通常希望在日志返回对应类名,但静态方法调用getClass的时候是调用this.getClass()而静态方法没有this。
合适的方法是使用以下语句:
new Object(){}.getClass().getEnclosingClass()
此处,new Object(){ }建立了Object的匿名子类的一个匿名对象,getEnclosingClass则得到其外围类,也就是包含这个静态方法的类
———————————————————————————————————————
静态内部类
有时候,使用内部类只是为了将一个类藏在另一个类的内部,并不需要内部类有外围类对象的一个引用。为此,可以将内部类声明为static。这样就不会生成那个引用。
下面是一个想要使用静态内部类的一个典型例子。任务:计算数组中的最大最小值
首先是方法的思路:遍历一次,记录最大最小值
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for(double v: values)
{
if(min > v)min = v;
if(max < v)max = v;
}
这个方法有两个值需要返回,为此可以定义一个包含两个double值的类
class Pair
{
private double first;
private double second;
public Pair(double f,double s)
{
first = f;
second = s;
}
public double getFirst(){ return first; }
public double getSecond(){ return second;}
}
minmax方法可以返回一个Pair类型的对象
class ArrayAlg
{
public static Pair minmax(double[ ] values)
{
...
return new Pair(min,max);
}
}
为了防止重名,最好将Pair定义为内部类,由于是在静态方法中定义,没有办法返回外围类的引用,所以应该使用静态内部类
class ArrayAlg
{
public static class Pair
...
}
...
}
- 只要内部类不需要访问外围类对象,一般都是使用静态内部类,一些C++沿用过来的习惯是叫它嵌套类
- 与常规内部类不同,静态内部类可以有静态字段和方法
- 接口中的内部类自动是static和public
成品:
package 内部类;
/*This program demonstrates the use of static inner classes
* demonstrates:演示
* @version 1.0 2020/6/24
* @author 乐乐想学会java
*
*
* 本程序中出现的标示
* @version 版本描述
* @author 作者描述
* @param 自由文本
*
*
*
* 常用的还有 @see@link,用于创建一个超链接 @see 包路径下某类#方法(double),链接到该方法
* 或<a href ="... ">label</a>的html标签类型
* 或直接" "
* */
public class StaticInnerClassTest {
public static void main (String []args)
{
//var values = new double[20];貌似版本不是java10,那就不能用var了
double[] values = new double[20];
for(int i=0;i<values.length;i++)
values[i]=100*Math.random();//生成随机数
ArrayAlg.Pair p = ArrayAlg.minmax(values);
System.out.println("min="+p.getFirst());
System.out.println("max="+p.getSecond());
}
}
class ArrayAlg
{
/*
* A pair of floating-point numbers
* */
public static class Pair
{
private double first;
private double second;
/*
*Constructs a pair from two floating-point numbers
* @param f the first number
* @param s the second number
* */
public Pair(double f,double s)
{
first = f;
second = s;
}
/*
* Returns the first number of the pair
* @return the first number
* */
public double getFirst()
{
return first;
}
/*
* Returns the second number of the pair
* @return the second number
* */
public double getSecond()
{
return second;
}
}
/*
* Computes both the minimum and the maximum of an array
* @param values an array of float-point numbers
* @return a pair whose first element is the minimum and whose second element is the maximum
*
* */
public static Pair minmax(double[] values)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for( double v :values)
{
if(min>v)min=v;
if(max<v)max=v;
}
return new Pair(min,max);
}
}