内部类

今日知识总结;

内部类

一个类或者接口定义在另外一个类或者接口的内部

public class A1{//外部类
class B1{}//内部类
interface C1{}//内部接口
}

将一个类定义置入另一个类定义中,这就叫作“内部类”

  • 内部类之外的类称为外部类
  • 内部类的名称必须区别于它所在的外部类,和其它类之间没有要求
//内部类的全名叫做[外部类名称$内部类名称]
public class A1 {
public class A1{} //语法报错
class B1{} //语法正确,默认编译的结果名称为A1$B1.class
}
class B1{}
  • 内部类可以访问其外部类的所有变量和方法
public class A1 {//外部类的范围限定词只能是public或者默认package
private String name;
public class B1 { //内部类的范围限定词可以是4种
public void pp() {
System.out.println(name); //可以直接访问外部类的成员
System.out.println(A1.this.name);//这里的A1.this用于表示A1类对象
A1.this.name="ffff";
// System.out.println(this.name);报错的原因是this用于指代当前类的对象,当前类
B1中并没有属性name
pp(); //可以直接访问外部类的成员
A1.this.pp();
}
}
private void pp(){}
}
  • 外部类不能直接访问内部类的实现细节,可以通过创建内部类对象的方式直接访问,不受限定词的影响
public class A1 {
private String name;
public class B1 {
private int age=99;
public void ff() {
System.out.println(name);
System.out.println(A1.this.name);
A1.this.name="ffff";
}
private void dd(){}
}
private void pp(){
B1 b=new B1(); //如果需要在外部类中访问内部类的实现细节则需要构建内部类对象,然后
通过内部类对象进行访问
b.ff();
System.out.println(b.age); //注意:可以直接访问内部类中的私有属性
b.dd();//私有方法仍旧可以直接调用
}
}
  • 内部类比外部类多了private/protected/static三个修饰符,这三个修饰符不能用在外部类上
  • 非静态内部类不能拥有静态成员,静态内部类则没有这个限制
public class A{
protected class B1 {
private int age = 99;
{ }//允许包含非静态代码块
private static String password="123456";//非静态内部类中不能包含静态属性
static{ }//非静态内部类中不允许包含静态代码块
public B1() {//允许定义构造器和析构器方法
}
public static void hh(){} //非静态内部类中不允许包含静态方法
}
}

内部类的作用

内部类提供更好的封装
内部类可以直接访问外部类的私有成员,外部类不能直接访问内部类的成员
匿名内部类适合用于创建仅仅使用一次使用的类

内部类相关的设计

分析事物时发现该事物描述还有事物,而且这个事物还在访问被描述事物的内容
例如牛和牛腿
如果一个事物离开另外一个事物后则没有任何意义,这种情况下建议使用内部类,不允许其他类访问
内部类能直接访问外部类中成员,是因为内部类持有了外部类的引用,即外部类名.this

内部类分类

在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。

内部类实际拥有外部类的一个引用,在构造函数中将外部类的引用传递进来。

非静态内部类

public class A1{
protected class B1{}//静态内部类是protected static class B1{}
}

在创建非静态内部类对象时,一定要先创建起相应的外部类对象

public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc = b.new C1(); //内部类的写法为[外部类名.内部类名],否则需要import
com.yan.B1.C1;则可以直接使用C1类。至于C1类是否可见取决于C1类的范围限定词
System.out.println(cc);
}
}
class B1 {
public class C1 {
}
}

在任何非静态内部类中,都不能有静态数据、静态方法或者又一个静态内部类

//语法错误
public class A1{
public class B1{
public static String name="yan";//语法错误,除非B1类是静态内部类 static class
B1{}
static{}//语法错误
public static void pp(){}//语法错误
public static class C1{}//语法错误
}
}

注意内部类的可见性范围限制
访问方法:直接访问外部类中内部类中的成员

class B1 {
private String pwd;
private void dd(){}
protected class C1 {
private String name;
private void pp(){
System.out.println(pwd);//内部类可以直接访问外部类中的私有成员
//另外的写法 B1.this.pwd
dd();
//另外的写法 B1.this.dd();
}
}
public void ee(){
//如果需要访问内部类中的成员,则必须首先创建内部类对象
C1 cc=new C1();
//通过构建的内部类对象则可以访问内部类中的私有成员
cc.name="abc";
cc.pp();
}
}

外部类.内部类 in=new 外部类().new 内部类();

public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc=b.new C1();//内部类是否可以访问到,取决于内部类上的范围限定词
}
}
class B1 {
class C1{}
}

in.内部类方法();

public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc=b.new C1();
cc.pp(); //是否可以访问pp方法取决于pp方法的范围限定词
}
}
class B1 {
class C1{
public void pp(){}
}
}

注意内部类的可见性范围限制

class B1 {
private class C1{//注意这个C1类只能在B1类中进行使用,其它位置不可见
public void pp(){}
}
}

问题:

public class A1 {
public A1(){
System.out.println("aaaaa");
}
public static void main(String[] args) {
new A1();//创建外部类对象和内部类无关,创建外部类对象并不会自动创建内部类对象
}
class C1 {
public C1() {
System.out.println("dddd");
}
}
}

静态内部类

public class A1{
public static class B1{}//静态内部类
}

静态内部类中,可以有静态数据、静态方法或者又一个静态内部类

class B1 {
static class C1 {// 在静态内部类中才可以定义静态成员
private static int age = 123;
private String pwd="123456";//同时允许定义非静态成员
static {
System.out.println("c1 static...");
}
public static void pp() {
System.out.println("c1 static pp...");
}
public void ff(){//同时允许定义非静态成员
}
static class D1{}
class E1{}
}
}

如何使用静态内部类

public class A1 {
public static void main(String[] args) {
B1.C1 cc = new B1.C1();//不需要构建外部类对象,就可以直接创建静态内部类对象,但是是
否可见取决于范围限定词
B1.C1.pp();//可以直接访问静态内部类中的静态成员,但是是否可见取决于范围限定词
cc.ff();
}
}

问题

public class A1 {
public static void main(String[] args) {
B1.C1 cc = new B1.C1();
}
}
class B1 { //外部类
static{
System.out.println("b1 static...");
}
private B1() {
System.out.println("bbbbbb");
}
static class C1 {// 在静态内部类中才可以定义静态成员
public C1() {
System.out.println("cccccc");
}
static {
System.out.println("c1 static...");
}
//输出只有
//c1 static... 类加载完成后自动执行,不会执行外部类的静态块b1 static..."
//cccccc 构建静态内部类对象和外部类无关,没有要求必须先构建外部类对象

静态内部类中,也可以有非静态数据、非静态方法或者又一个非静态内部类
静态内部类中,不能访问外部类的非静态成员,这是由Java语法中【静态方法不能直接访问非静态成员】所限定

class B1 {
private static String name1 = "yan1";
private double salary = 123.456;
static class C1 {
private static int age = 123;
private String pwd = "abc";
public static void pp() {
System.out.println(age);// 静态方法只能访问静态成员
// System.out.println(this.age);在靜態方法中不允許使用this或者super
// System.out.println(pwd); 语法报错,因为静态方法只能访问静态成员
System.out.println(name1);// 静态内部类中可以直接访问外部类中的静态成员
dd();
// System.out.println(salary);语法报错,因为静态方法只能访问静态成员
}
public void fff() {
System.out.println(this.age);
System.out.println(pwd);//当前类中的非静态成员
System.out.println(name1);// 不允许使用B1.this或者this.进行访问,不允许使用
B1.this是因为构建C1类对象时没有要求必须构建外部类对象
// System.out.println(salary);报错的原因是构建构建C1类对象时没有要求必须构建外
部类对象
dd();
// ee();z直接访问ee方法报错,构建构建C1类对象时没有要求必须构建外部类对象
}
}
private void ee() {
}
private static void dd() {
}
}

问题1:

class B1 {
static class C1 {
}
}
构建内部类对象的方法:
B1.C1 cc = new B1.C1();
以下创建方法错误:
B1 b = new B1();
B1.C1 c = b.new C1();

问题2:

public class A1 {
public static void main(String[] args) {
B1.C1 bcc=new B1.C1();
}
}
class B1 {
private static D1 dd=new D1();
static {
System.out.println("B1 static....");
}
static class C1 {
private static E1 dd=new E1();
static {
System.out.println("C1 static...");
}
}
}
//这里执行会发现B1类的静态代码块和静态属性并没有执行处理,所以要理解外部类实际上是内部类的一个名空间。加载内部类时实际上并没有加载外部类----重点

局部内部类

可以将内部类定义在一个方法或者一个代码块内

public class A1 {
private int age = 123;
public static void main(String[] args) {
A1 aa = new A1();
}
public void pp() {
Date birth = new Date();
// B1 bb=new B1();语法错误,要求局部内部类必须先定义后使用
class B1 { // 局部内部类,只能在所在的{}范围中使用,具备内部类的范围限定词和临时变量一致,
只能添加final或者abstract
public void ff() {
System.out.println(age);
A1.this.age = 555;// 可以直接访问外部类中的成员
System.out.println(birth);
// 语法报错,在局部内部类中使用外部的临时变量,则外部临时变量必须是final的,只是
final可以省略
// birth = new Date();针对引用类型修改地址是不允许,但是可以修改属性
System.out.println("B1...ff()");
}
}
B1 bb = new B1();
}
}

问题:

public class A1 {
private int age = 123;
public static void main(String[] args) {
A1 aa = new A1();
}
public void pp() {
static class B1 {//局部内部类不能是static
static int age = 999;//不允许包含静态成员
static{}
public static void ff(){}
}
}
}

注意:局部内部类需要先定义后使用,不能是先使用后定义

匿名内部类

匿名内部类就是内部类的简写格式

public class A1{
public void pp(){
class B1 extends C1{} //定义内部类时实际上允许继承于其他类或者实现特定接口
}
}
//简化写法
public class A1{
public void pp(){
new C1(){};//原始命名类的写法class B1 extends C1{} new B1();
}
}
public class A1 {
public void pp(){
new Object(){
public void ff(){ //ff方法在父类中没有定义,则只能直接调用
System.out.println("anon inner class ... ff");
}
}.ff();
}
public static void main(String[] args) {
A1 a1=new A1();
a1.pp();
}
}

下面写法使用较多

public class A1 {
public void pp(){
Object obj=new Object(){
@Override//覆盖父类中定义的方法
public String toString() {
return ("anon inner class ... ff");
}
};
System.out.println(obj.toString());//调用匿名内部类中覆盖定义的方法
}
}

匿名内部类的前提是必须继承或者实现一个外部类或者接口

new interfacename(){......};
new superclassname(){......};
如果父类中没有无参构造器,则()中应该有对应的参数
public class A1 {
public void pp(){
new B1(20){ //因为B1类中没有无参构造器,必须直接传入参数
};
}
}
class B1{
private int age;
public B1(int age){
this.age=age;
}
}

匿名内部类由于没有名字,所以它没有构造函数

如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数

不能定义静态成员

匿名内部类的使用场景

当方法参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数进行传递

public interfce IA{
public void cc();
}
public class B{
public void pp(IA a){
}
}
public class Test{
public static void main(String[] args){
B b=new B();
b.pp(new IA(){
public void cc(){}//提供接口IA中的所有抽象方法的实现
});
}
}

匿名内部类的使用限制

匿名内部类不能是抽象的

匿名内部类不能定义构造器,默认和父类相似的构造器

JDK1.8-要求给局部内部类、匿名内部类访问的局部变量必须使用Õnal修饰,从JDK1.8开始这个现实被取消了,但是默认是Õnal的(不能修改)

public void pp() {
final Date birth = new Date();
class C1 {
public void ff() {
System.out.println(birth);
birth.setYear(2000);// 3900
// birth=new Date(); 语法报错
System.out.println("modify year:" + birth);
}
}
new C1().ff();
}

int8种简单类型/Integer8种简单类型的包装类/String的使用中应该注意

class A11 {
public void pp() {
final Date now=new Date();
final String str="abcde";
int[] arr= {1,2,3,4};
int kk=100;
new Object() {
@Override
public String toString() {
//kk++; 针对简单类型则final表示值不可变
//针对引用类型表示地址不可变
arr[0]=999; //arr=new int[] {3,4,5,6};对象新建了所有报错
now.setYear(1900);//now=new Date()也会报错
str+="ddd";//针对String类型的修改操作会引发对象的新建
return now.toGMTString();
}
}.toString();
}
}

典型的匿名内部类使用场景:

/*
* JFrame窗口。JFrame 是一个可以独立显示的组件,一个窗口通常包含有标题、图标、操作按钮(关
闭、最小化、最大化),还可以为窗口添加菜单栏、工具栏等。一个进程中可以创建多个窗口,并可在适当
时候进行显示、隐藏 或 销毁。
*/
public class Test1 {
public static void main(String[] args) {
JFrame mainWin = new JFrame("窗口标题"); //创建窗口对象
mainWin.setSize(800, 600); //设置窗口对象的宽窄
JPanel panel = new JPanel(); //构建panel,即一个矩形区域对象
JButton btn = new JButton("Show New Window"); //构建按钮
btn.addActionListener(new ActionListener() { //添加按钮对应的事件处理
public void actionPerformed(ActionEvent e) {
showNewWindow(mainWin);
}
});
panel.add(btn);
mainWin.setContentPane(panel);
mainWin.setVisible(true);
}
public static void showNewWindow(JFrame relativeWindow) {
// 创建一个新窗口
JFrame newJFrame = new JFrame("新的窗口");
newJFrame.setSize(250, 250);
// 把新窗口的位置设置到 relativeWindow 窗口的中心
newJFrame.setLocationRelativeTo(relativeWindow);
// 点击窗口关闭按钮, 执行销毁窗口操作(如果设置为 EXIT_ON_CLOSE, 则点击新窗口关闭按
钮后, 整个进程将结束)
newJFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// 窗口设置为不可改变大小
newJFrame.setResizable(false);
JPanel panel = new JPanel(new GridLayout(1, 1));
// 在新窗口中显示一个标签
JLabel label = new JLabel("这是一个窗口");
label.setFont(new Font(null, Font.PLAIN, 25));
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
panel.add(label);
newJFrame.setContentPane(panel);
newJFrame.setVisible(true);
}
}

内部类的使用场景和好处

每个内部类都能独立的提供一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整

方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏

public class{
private class 牛腿{}
}

方便编写事件驱动程序

btn.addActionListener(new ActionListener() { //添加按钮对应的事件处理
public void actionPerformed(ActionEvent e) {
showNewWindow(mainWin);
}
});

方便编写线程代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值