黑马程序员——接口、克隆、内部类

来源:互联网 发布:2016淘宝详情尺寸大小 编辑:程序博客网 时间:2024/06/06 09:06
------- android培训、java培训、期待与您交流! ----------

一、接口

(一)接口的定义

用来描述类具有什么功能,而不给出功能的具体实现。

interface InterfaceName

{

         typenamefunction(…);

 

}

接口所有方法自动属于public接口中的域自动设为publicstatic final

接口不能含有实例域(只是实例域不可以),不能实现方法 

(二)接口的实现

类在实现接口时

1将类声明为实现给定的接口

2对接口中的所有方法进行定义

class ClassName implements InterfaceName

{

         typenamefunction(…){    }

}

实现接口时,必须把实现的方法都标记为public,不然会被当作默认权限,而接口中自动为public,则实现的方法的权限小于接口的方法,会出错

 (三)compareTo父子类型比较问题

Comparable中compareTo的方法,对于double类型不能用相减来快速得到。应当使用

Double.compare(x,y)

 

compareTo也可能像equals一样出现父子类型比较的问题。

因为子类扩展了超类,然而超类实现的是Comparable<FatherClass>,

如果子类需要覆盖compareTo,就需要考虑超类和子类对象进行比较的情况。不能仅仅将超类转换成子类(compareTo的参数需传入超类对象的情况)

解决办法

1.若子类之间的比较含义不一样,必须先用

if(!(getClass()==other.getClass())) thrownew ClassCastException(); 

2若存在一种通用算法能够比较不同的子类对象,则应该在超类中提供这个方法并且声明为final 

(四)接口特性

1不能用new实例化

2但是可以声明一个接口的对象变量

3但是接口的对象变量必须引用一个实现了接口的类的对象

4使用 instanceof检查一个类对象是否属于某个接口


二、对象克隆

对象克隆 - 深拷贝与浅拷贝的问题

当拷贝一个类类型变量时,原变量与拷贝变量同时引用同一个对象。改变其中一个变量所引用的对象(的状态)会对另一个变量产生影响

 

为了复制出一份独立的副本应当使用克隆技术。Object类的clone()方法(浅拷贝),可以判断调用对象的类型,在内存中开辟一块与调用对象大小相同的区域将调用对象的域复制过去。

复制的是域的值,若域为对象变量,并不会将其所引用的对象一同复制,而是原对象和拷贝对象都引用同一个对象,即浅拷贝

 

深拷贝:覆盖Objectclone方法,在方法中复制可变的对象。

 

Object类的clone()方法

protected Object clone() throwsCloneNotSupportedException

Object没有实现Cloneable接口,方法是protected,保护权限clone()方法在子类、自定义类无法直接使用,需要覆盖。在构造自定义类时覆盖此方法应当修改为public

 

自定义类实现克隆的步骤:

1.声明Cloneable接口

2.覆盖clone()方法

3.覆盖的clone()的权限提升到public

4.返回值修改为自定义类(5.0后协变返回类型)

5.记住抛出CloneNotSupportedException异常

6.调用super.clone()并将返回值强制转换为自定义类

7.深拷贝:单独对可变对象进行克隆,将副本传给新建的自定义类的克隆对象。

8.在使用自定义类的clone()方法时,总记得将返回值强制类型转换为自定义类。

//有类Person,子类Student,让Person和Student实现克隆import java.util.Date;class Person implements Cloneable{private String name;private Date birthday;//...public Person clone()throws CloneNotSupportedException{Person cloned=(Person)super.clone();cloned.birthday=(Date)birthday.clone();return cloned;}}class Student extends Person implements Cloneable{private int id;//...public Student clone() throws CloneNotSupportedException{Student cloned=(Student)super.clone();cloned.id=id;return cloned;}}

除非是final类,否则覆盖的clone()方法不应该捕获CloneNotSupportedException,而是应当抛出。如果不支持克隆,该类可能的子类应当有抛出此异常的选择权

 

自定义类无法直接使用clone()的原因

首先Object的clone()权限为protected。一般来说protected对包内和子类可见。但是这个说法是笼统的。

 

具体来说,protected权限对于“不同包的子类,只能够访问其自身的继承自超类的protected域和方法,无法访问超类的实例的protected权限的域和方法”,某一个子类只能通过本类或本类的子类的实例来访问超类的protected方法,而不能通过超类或者超类的其他子类的实例来访问超类。

注意是自身的,从超类继承后看作是自身的一部分,在类的内部代码中相当于一个自定义的protected方法,在此类内部的代码可以直接访问这个protected方法。

//test包中package test;public class MyObject3 {protected Object clone() throws CloneNotSupportedException {       return super.clone();}}//默认包中import test.*;public class test3 extends MyObject3 {    public static void main(String args[]) throws CloneNotSupportedException{       MyObject3 obj = new MyObject3();       obj.clone(); // Compile error.不能通过超类实例调用超类保护权限方法       test3 tobj = new test3();       tobj.clone();// Complie OK.通过本类实例调用超类保护权限方法    }}


三、内部类

定义在另一个类中的类

注意:

1.内部类在外部类内部,但不是每个外部类对象都有一个内部类的实例域。

2.内部类ctor自动添加一个外部类的引用。在内部类中可以用OuterClass.this表示 

(一)使用原因

1.可以访问内部类所在的作用域的所有数据。包括私有

2.对同一个包中的其他类隐藏

3.定义了一个回调函数且不想编写大量代码

(二)访问规则

1.内部类可以访问外部类所有权限的域和方法

2.外部类想要使用内部类的域和方法,须要先建立一个内部类对象,通过内部类对象调用。

(三)访问格式

1.在内部类中引用外部类:OuterClass.this

2.创建一个新的内部类对象:OuterObject.newInnerClass(…)或者this.newInnerClass(…)(在外部类内部的方法中使用)或者newOuterClass.newInnerClass(…)内部类是公有类,在外部类外部创建一个内部类对象

3.在外部类外创建一个指向内部类对象的引用,应当使用OuterClass.InnerClass表示内部类名。比如,Map.Entry

(四)内部类的位置与权限

1.成员位置,公有,使用方法见上;私有,只有外部类内部可见;静态:

static静态内部类

使用静态内部类是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外部类对象。

静态内部类没有对外部类的引用OutClass.this,只能访问外部类的静态成员。

 

声明在接口中的内部类自动成为staticpublic

 

与abstract相同,若内部类中有静态成员,内部类必须声明为static

外部类的静态方法可以访问内部类的话,内部类也必须是静态内部类。(外部类的静态方法先于外部类对象的产生,在没有外部类对象的情况下能够访问内部类,说明这个内部类也是静态的)

 

外部类访问静态内部类的非静态成员:

new OuterClass. InnerClass().func();

外部类访问静态内部类的静态成员:

OutClass.InnerClass.func();

 

2.局部位置:局部内部类:

         定义在外部类的某个方法中

         不能用static,public或者private修饰,作用域被限定在声明局部类的块中。

         完全对外不可见,外部类的其他代码也无法访问。

         除了可以访问外部类,还可以访问调用局部内部类的方法中的局部变量。

         但是必须将局部变量用final修饰:注意,final修饰并不是说变量的值是固定的一个,而是指创建这个变量后,只能够为之赋值一次,之后无法再修改

         如果想修改,不能用对象包装器,因为其中的值也是不可变的,应当使用长度为1的数组,将数组引用传给final定义的局部变量,表示引用的指向不可改变,但是数组中元素的值可以修改。

class Outer{      int x = 3;      void method(final int a) { <span style="white-space:pre"></span>final int y = a;//方法一调用就须先将final修饰的y初始化。/*final int y = a;必须在class Inner之前,因为在Inner中使用了局部变量y。当方法被调用时,局部内部类被加载,function中需要访问y,而若final int y=a;在class定义之后,则在加载Inner时,尚不存在y,则会报错。由此可见,虽然是final修饰y,但是y初始化的值是通过调用方法method的参数传入的。*/<span style="white-space:pre"></span>class Inner{            void function(){                System.out.println(y);              }          }          <span style="white-space:pre"></span>new Inner().function();     }  }  class  InnerClassDemo  {      public static void main(String[] args)       {          Outer out = new Outer();          out.method(7);//打印7          out.method(8);//打印8      }  }

public class Clock{public void start(int interval,final boolean beep){class TimePrinter implements ActionListener{public void actionPerformed(ActionEvent event){Date now=new Date();System.out.println(now);if(beep){Toolkit.getDefaultToolkit().beep();}}}ActionListener listener=new TimerPrinter();Timer t=new Timer(interval,listener);t.start();}}

此处的内部类TimePrinter需要访问外部类传入的局部变量beep,当beep为真才会响。

当方法被调用,内部类的ctor被调用,访问了被定义成final的局部变量beep,这个变量在构造listener,构造t,调用start()方法后,外部的start()方法结束,这个局部变量就回收了。

然而内部类的actionPerformed方法仍然存在,且使用时需要访问beep,所以TimerPrinter类在beep释放前备份了beep。

(五)匿名内部类

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

定义匿名内部类的前提:

内部类必须是继承一个类或者实现接口。

              特殊情况:因为所以的类都有一个父类Object,所以在定义时也可以用Object。

匿名内部类的格式:  new父类或者接口(){定义子类的内容}

其实匿名内部类就是一个匿名子类对象。可以理解为带内容的对象。

匿名内部类中定义的方法最好不要超过3个。

匿名内部类的利与弊:

       好处:简化书写

       弊端:1、不能直接调用自己的特有方法、

2、不能做强转动作。

3、如果继承的父类或接口中有很多方法时,使用匿名内部类阅读性会非常差,且调用会很麻烦。所以匿名内部类中定义的方法有一般不超过3个。




0 0
原创粉丝点击