(九) 面向对象 - 内部类

一、内部类介绍

 

一、内部类介绍

一个类定义在另一个类的内部,前者称之为内部类,后者称之为外部类。

 
内部类一般用在定义它的类和语句块内,在外部使用它时需要给出完整的名称,且不可以与外部类的名称相同。

 
使用场景为当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使 用内部类。比如:集合中的某些结构

 
 

二、内部类特点

内部类是一个编译器行为,与虚拟机无关。编译器会将内部类转换为常规的类文件,用$分隔外部类类名与内部类名,而虚拟机对此一无所知。

 
内部类可以对同一个包下的其他类隐藏(只有内部类可以是private修饰,而常规类可以有包可见性(缺省)与公共可见性(public))。

 
内部类可以访问定义所在的这个类的作用域中的数据,包括原有的私有数据。

 
 

三、内部类分类

​ 静态内部类

​ 非静态内部类

​ 局部内部类

​ 匿名内部类

 
 

二、静态内部类

 

一、特点:

Inner class 可以声明为private 和protected、缺省、public,final(最终类),abstract(可被继承)。
 
Inner class 可以定义成员变量,构造方法,方法(可以是static静态的)
 
Inner class 并不 包含一个隐式引用,指向实例化这个对象的外部类对象。
 
Inner class 可以访问out class 的属性,方法(包括私有的)。
 
Out calss 的静态成员部分使用内部类时,可以考虑内部类声明为静态的。

 
 

二、注意点:

只要内部类不需要访问外围类对象,就应该使用静态内部类。

 
在域之外,引用内部类:OutClass.InnerClass

ArrayAlg.Pair pair = new ArrayAlg.Pair(1.1, 1.2);

 
 

三、示例:

public 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 void setFirst(double first) {
            this.first = first;
        }

        public double getSecond() {
             return second;
        }

        public void setSecond(double second) {
             this.second = second;
        }
	}
		
	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);
		  }
		
	}
		 
		
	public static void main(String[] args) {
		        ArrayAlg.Pair minmax = ArrayAlg.minmax(new double[]{1.1, 4.5, 6.7, 8.9});
		        System.out.println("min = " + minmax.getFirst() + " max = " + minmax.getSecond());
		    }
}

 
 

三、非静态内部类

 

一、特点:

Inner class 可以声明为private 和protected、protected、缺省,final(最终类),abstract(可被继承)。
 
Inner class 可以定义成员变量,构造方法,方法,但不可以定义为static的成员。
 
Inner class 包含一个隐式引用,指向实例化这个对象的外部类对象。
 
Inner class 可以访问out class 的属性,方法(包括私有的)。

 
 

二、注意点:

 
关于隐式引用:

这个引用在内部类的定义中是不可见的。

外部类的引用在构造器中设置。构造器会修改所有的内部类构造器,添加一个外部类引用的参数。

 
在域之外,引用内部类:OutClass.InnerClass

 
 

三、示例

//语音时钟类
public class TalkingClock {
	private int interval;
	private boolean beep;
		
	public TalkingClock(int interval, boolean beep) {
		 this.interval = interval;
		 this.beep = beep;
	}
		
	//开始时钟
	public void start(){
		TimePrinter listener = new TimePrinter();
		Timer timer = new Timer(interval, listener);
		timer.start();
		
	}
		
	public class TimePrinter implements ActionListener{
		
		 @Override
		 public void actionPerformed(ActionEvent e) {
		     System.out.println("现在时间是:" + Instant.ofEpochSecond(e.getWhen()));
		
		     if(beep) Toolkit.getDefaultToolkit().beep();
		   }
		
	}
		 
		
	public static void main(String[] args) {
		TalkingClock clock = new TalkingClock(1000, true);
		clock.start();
		
		//一直让程序运行,直到你确定结束
		JOptionPane.showMessageDialog(null,"Quit");
		System.exit(0);
		
	}
}
		 

 
 

四、局部内部类

 

一、特点:

声明局部内部类不能有访问说明符(public 或private )。局部内部类的作用域被限定在声明这个局部类的块中。

 
只能够定义在方法和代码块内部,而且是先声明后使用。

但它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型。
 

局部内部类可以使用外部方法的局部变量,但是必须是事实的最终变量,即一旦被赋值就不可改变。由于局部内部类和局部变量的声明周期不同所致。

 
 

二、示例

public class TalkingClock1 {
		
	//开始时钟
	public void start(int interval, boolean beep){
		
		
		 //局部内部类
		 class TimePrinter implements ActionListener {
		
		      @Override
		      public void actionPerformed(ActionEvent e) {
		          System.out.println("现在时间是:" + Instant.ofEpochSecond(e.getWhen()));
		           if(beep) Toolkit.getDefaultToolkit().beep();
		         }
		      }
		
		    TimePrinter listener = new TimePrinter();
		    Timer timer = new Timer(interval, listener);
		    timer.start();
		
	}
		    
}

 
关于局部变量在局部内部类中使用的说明:

编译器检测到局部变量的访问,为每一个变量创建相应的实例字段,并将局部变量复制到构造器,从而初始化这些实例字段。

class TalkingClock1$1TimePrinter{

           TalkingClock1$1TimePrinter(TalkingClock1,boolean);

           public void actionPerformed(java.awt.event.ActionEvent);
         
           final boolean val$beep;

           final TalkingClock1 this$0;
}

 
此处的beep在局部内部类中被使用,因此会为其创建一个实例字段,并通过构造器初始化。

 
 

六、匿名内部类

 

一、概念

使用局部内部类,如果只想创建这个类的一个对象,设置不需要为类指定名字,这样的一个类称之为匿名内部类。

一个匿名内部类一定是在new的后面,用其隐含实现一个接口或 实现一个类。

 
 

new SuperType(参数列表){
    //内部类方法与数据
}

如果superType是一个类,那么内部类就要扩展这个类。

 
 
如果superType是一个接口,那么内部类就要实现这个接口。

同时由于构造器的名称必须与类名相同,而匿名内部类没有类名,因此匿名内部类没有构造器。

实际上构造参数是要传递给超类构造器,因此内部类实现一个接口,就不能有任何参数(接口无构造器方法),但仍然提供一对小括号。

new InterfaceType(){
    //方法与数据
}

 
 
尽管匿名类不能有构造器,但可以提供一个对象初始化块。

new SuperType(参数列表){
    {
        
    }
    //内部类方法与数据
}

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。

 
 

二、示例

public class Test{


    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("hello");
        list1.add("word");
        print(list1);

		//或
        //双括号初始化,外层括号建立了ArrayList的一个匿名子类,内层括号则是一个对象初始化块
        print(new ArrayList<String>(){
            {
                add("hello");
                add("word");
            }
        });

    }

    public static void print(ArrayList<String> lists){
        for(String list:lists){
            System.out.println(list);
        }
    }
}

 
注意:建立一个匿名子类通常会很方便,但对于以下equasl方法使用匿名子类可能会失败

....
   if(getClass() != other.getClass()) return false; 
...    

 
 

public class Test {



    public static void main(String[] args) {


        TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {

            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o2,o1);
            }
        });

        set.add(1);
        set.add(2);
        set.add(3);

        System.out.println(set);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值