一.概述
上一篇讲了lambda表达式,它可以让编码量降低,但不要忘记保持代码的可读性。链接:Java之lambda表达式
这篇讲内部类,为什么使用内部类呢?
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其它类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
二.使用内部类访问对象状态
1.基本使用
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
/**
* 内部类访问对象状态
*/
public class InnerClassDemo {
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000,true);
clock.start();
JOptionPane.showMessageDialog(null,"Quit program?");
System.exit(0);
// 创建内部类对象
TalkingClock.TimerPrinter printer = clock.new TimerPrinter();
}
}
class TalkingClock{
/**
* 时间间隔
*/
private int interval;
/**
* 开关铃声
*/
private boolean beep;
public TalkingClock(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start(){
// 当外围类创建内部类的时候,会将自身的引用this传递给内部类
// ActionListener listener = new TimerPrinter(this);
// 正确的编写创建内部类的语法
// ActionListener listener = this.new TimerPrinter();
ActionListener listener = new TimerPrinter();
Timer t = new Timer(interval,listener);
t.start();
}
/**
* 内部类
* 它拥有一个对外部类的引用,这个引用是不可见的.
* 编译器修改了所有的内部类的构造器,添加一个外围类引用的参数。
* 只有内部类可以将访问控制权限改为private这样代表只有TalkingClock类创建该类对象
*/
public class TimerPrinter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
// 可以直接访问外围类对象的数据域
// TalkingClock.this.beep 可以简写
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
}
2.语法规则
内部类中声明的所有静态域都必须是final。因为如果这个域不是final的,它可能就不是唯一的。
内部类不能有static方法。也可以允许有静态方法,但只能访问外围类的静态域和方法。
3.内部类信息和安全
内部类是一种编译现象,与虚拟机无关。编译器会把内部类翻译成用$分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。
// 测试内部类的具体信息
Class c1 = TalkingClock.TimerPrinter.class;
// 调用反射章节的封装方法
ReflectDemo.getClassInfo(c1);
// 结果
// public class com.gyx.demo.chapter11.TalkingClock$TimerPrinter
// {
// public com.gyx.demo.chapter11.TalkingClock$TimerPrinter(com.gyx.demo.chapter11.TalkingClock);
//
// public void actionPerformed(java.awt.event.ActionEvent actionPerformed );
// 编译器为了引用外部类会自动生成一个实例域(名字由编译器合成,自己编写的代码不能使用)
// final com.gyx.demo.chapter11.TalkingClock this$0
// }
Class c2 = TalkingClock.class;
// 调用反射章节的封装方法
ReflectDemo.getClassInfo(c2);
// 结果
// class com.gyx.demo.chapter11.TalkingClock
// {
// public com.gyx.demo.chapter11.TalkingClock(int, boolean);
//
// 编译器为外围类添加的静态方法,将返回作为参数返回外围类的beep
// static boolean access$000(com.gyx.demo.chapter11.TalkingClock);
// 在进行判断时if (TalkingClock.access$000(TalkingClock)) 替换成 if (beep)来提高效率
// public void start();
//
// private int interval
// private boolean beep
// }
这样做会有安全问题,虽然access$000名字是编译器取的但同样可以使用来调用外部类的私有属性。这个方法是包可见性的,所以攻击代码可以放在同一个包中。
三.内部类的种类
1.局部内部类
放在方法中的内部类
public void start() {
// 如果内部类只被一个方法使用,可以使用局部内部类
// 局部内部类不能用public或private修饰符
// 优势:只对此方法可见,对外部完全隐藏
class TimerPrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
ActionListener listener = new TimerPrinter();
Timer t = new Timer(interval, listener);
t.start();
}
2.由外部方法访问变量
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
/**
* 访问方法的局部变量
*/
public class InnerClassDemo1 {
public static void main(String[] args) {
TalkingClock1 clock = new TalkingClock1();
clock.start(1000,true);
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class TalkingClock1 {
// 在Java8之前要想使用局部变量要将beep声明为final类型
public void start(int interval,boolean beep) {
class TimerPrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
// 可以访问start方法中的局部变量
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
// 在创建内部类对象的时候,内部类中会产生一个boolean类型的实例域,并把start方法中的局部变量传递给它并使用。
ActionListener listener = new TimerPrinter();
Timer t = new Timer(interval, listener);
t.start();
}
}
3.匿名内部类
假如只创建内部类的一个对象,就可以不必命名直接使用。
/**
* 匿名内部类
*/
public class InnerClassDemo2 {
public static void main(String[] args) {
TalkingClock2 clock = new TalkingClock2();
clock.start(1000,true);
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
// 双括号初始化 这个功能利用的是内部类 但是这样会使代码没有可读性,不推荐使用
clock.add(new ArrayList<String>(){{add("Harry");}});
}
}
class TalkingClock2 {
public void add(ArrayList<String> list) {
}
public void start(int interval,boolean beep) {
// 匿名内部类
// 因为匿名所以不能有构造器。
ActionListener listener = new ActionListener() {
// 匿名内部类
// 因为匿名所以不能有构造器。
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
// 可以访问start方法中的局部变量
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
};
// 使用lambda表达式更加简洁推荐使用
ActionListener listener1 = e -> {
System.out.println("At the tone,the time is" + new Date());
// 可以访问start方法中的局部变量
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
}
4.静态内部类
有时候我们只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。
/**
* 静态内部类
*
*/
public class StaticInnerClassDemo {
public static void main(String[] args) {
double[] d = new double[20];
for (int i = 0; i < d.length; i++) {
d[i] = 100 * Math.random();
}
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
class ArrayAlg {
/**
* 静态内部类
*/
public static class Pair {
private double first;
private double second;
public Pair(double first, double second) {
this.first = first;
this.second = second;
}
public double getFirst() {
return first;
}
public double getSecond() {
return second;
}
}
/**
* 因为需要将大数和小数一起返回,所以需要封装成一个对象,并且希望这个对象和定义其它类的对象有所区分
* @param values
* @return
*/
public static Pair minmax(double[] values) {
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double value : values) {
if (max < value) {
max = value;
}
if (min > value) {
min = value;
}
}
// 由于静态方法中需要创建内部类对象,所以内部类必须是static的
return new Pair(min, max);
}
}
四.总结
内部类可以更方便的进行类之间的访问,并且比常规类更灵活。但是也存在一定的安全问题。
下一篇讲代理的基本使用
有些可能我理解的不够深刻,大家如果觉得我说的不够详细可以参考我的推荐书,详细的看一下。欢迎大家评论。第一时间我会回复大家。谢谢!