java-接口与内部类

6.1接口
1.接口不是类,而是对类的一组需求描述,类要遵从接口描述的统一格式进行定义。
2.在接口中不能包含实例域或静态方法,不能在接口中实现方法,但可以包含常量。
3.不能使用new来实例化一个接口,但是可以生命一个接口的变量,接口变量必须应用实现了该接口的类对象。
4.如同使用instanceof检查一个对象是否属于某个特定的类一样,也可以使用instanceof检查一个对象是否实现了某个特定的的接口。
5.接口中的方法默认被标记为public,域被标记为public static final。
6.每个类只能拥有一个超类,但却可以实现多个接口。在实现多个接口时,使用逗号将接口分开。
7.实现接口使用关键字implements,使用Arrays类的sort方法对对象数组进行排序时,对象数组中的对象必须实现Comparable接口。
class XXX implements Comparable<xxx>{
	public int compareTo(XXX other){
		return Double.compare(x,other.x);   //x<other.x,则返回-1;x=other.x,则返回0;x>other.x,则返回1;
		//return id-other.id;               
        }
}

8.如果某个类实现了Cloneable接口,Object类中的clone方法皆可以创建类对象的一个克隆。
class XXX implements Comparable, Cloneable
9.问题:为什么java要引入接口概念,为什么不直接将Comparable设计成抽象类。
因为每个类只能扩展一个类,假设这个类已经扩展了一个类,就不能扩展第二个类了。Java是单继承的,不允许多继承,为了弥补这一缺陷,所以出现了接口的实现,主要的原因是多继承会使语言本身变得非常复杂。
6.2对象克隆
1.当拷贝一个变量时,如果只是简单地将原始变量赋值给拷贝变量时,原始变量和拷贝变量将应用通一个对象,任意改变这两个变量中的其中一个,将会对另一个产生影响。
Student stu1 = new Student(); 
	        stu1.setNumber(12345); 
	        Student stu2 = stu1; 
	System.out.println("学生1:" + stu1.getNumber()); 
	        System.out.println("学生2:" + stu2.getNumber()); 
	stu2.setNumber(54321); 
	 
	System.out.println("学生1:" + stu1.getNumber()); 
	System.out.println("学生2:" + stu2.getNumber()); 

学生1:12345
学生2:12345
学生1:54321
学生2:54321
2.如果想要两个变量各自改变而互不影响,就必须使用clone方法。Clone方法是Object类的一个protected方法。
protected native Object clone() throws CloneNotSupportedException;

因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问,不能直接调用。
要想对一个对象进行复制,就需要对clone方法覆盖。
3.如果对象中的所有数据域都属于数值或基本类型,浅拷贝没有任何问题。但是,如果在对象中包含子对象的引用,浅拷贝的结果会使得两个域引用同一个子对象,使得原始对象和拷贝对象共享这部分信息。如果原始对象和浅拷贝对象共享的子对象使补课变得或者是不允许改变的类,将不会产生任何问题。
4.浅拷贝
(1) 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
(2) 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
class Student implements Cloneable{ 
	    private int number; 
	    @Override 
	    public Object clone() { 
	        Student stu = null; 
	        try{ 
	            stu = (Student)super.clone(); 
	        }catch(CloneNotSupportedException e) { 
	            e.printStackTrace(); 
	        } 
	        return stu; 
	    } 
	} 

5.当原始对象中存在一对象的引用时,需要进行深拷贝,这样子对象的引用也不会被影响。
class Address implements Cloneable { 
	    private String add; 
	    @Override 
	    public Object clone() { 
	        Address addr = null; 
	        try{ 
	            addr = (Address)super.clone(); 
	        }catch(CloneNotSupportedException e) { 
	            e.printStackTrace(); 
	        } 
	        return addr; 
	    } 
	} 

	class Student implements Cloneable{ 
	    private int number; 
	 
	    private Address addr; 
	
	    @Override 
	    public Object clone() { 
	        Student stu = null; 
	        try{ 
	            stu = (Student)super.clone();   //浅复制  
	        }catch(CloneNotSupportedException e) { 
	            e.printStackTrace(); 
	        } 
	        stu.addr = (Address)addr.clone();   //深度复制  
	        return stu; 
	    } 
	}

6.所有的数组类型均包含一个clone方法,这个方法已经被设为public,而不是protected。
Int[] array = {2, 3, 4, 5, 6};
Int[] cloned = array.clone();

6.3内部类
1.内部类是定义在另一个类中的类。
2.需要使用内部类的原因:
(1)内部类方法可以访问该类定义所在的作用域(外围类对象)中的数据,包括私有的数据。
(2)内部类可以对同一个包中的其他类隐藏起来。
(3)当想要定义一个回调函数且不想编写大量的代码,使用匿名内部类比较便捷。
3.内部类分为四种:常规内部类,局部内部类,匿名内部类,静态内部类
【1】常规内部类
常规内部类不用static修饰且定义在在外部类类体中。
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
内部类的访问类型可以为public,private和默认的包可见性。
只有内部类可以是私有类,如果为私有类只有外围对象的方法可以构造内部类对象。
外围类引用:外围类.this,例如(TalkingClock.this.beep)
内部对象的构造器:外围类对象.new 内部类(),例如(ActionListener listener = this.new TimePrinter())
在外围类的作用域之外还可以这样引用内部类:OuterClass.InnerClass
编译器将会把内部类翻译成用$分隔外部类名与内部类名的常规类文件,虚拟机并不知晓,例如(TalkingClock$TimePrinter.class)。
public class MyOuter {
 private int x = 100;
 // 创建内部类
 class MyInner {
  private String y = "Hello!";
  public void innerMethod() {
   System.out.println("内部类中 String =" + y);
   System.out.println("外部类中的x =" + x);// 直接访问外部类中的实例变量x
   outerMethod();
   System.out.println("x is" + MyOuter.this.x);
  }
 }
 public void outerMethod() {
  x++;
 }
 public void makeInner() {
//在外部类方法中创建内部类实例
  MyInner in = new MyInner();
 }

 public static void main(String[] args) {
  MyOuter mo = new MyOuter();
  // 使用外部类构造方法创建mo对象
  MyOuter.MyInner inner = mo.new MyInner();//常规内部类需要通过外部类的实例才能创建对象,与实例变量需要通过对象来访问相似
  // 创建inner对象
  inner.innerMethod();
  // TODO Auto-generated method stub
 }
}

【2】局部内部类
如果一个内部类在方法中创建这个类型的对象值使用了一次,就使用局部内部类。在方法体或语句块(包括方法、构造方法、局部块或静态初始化块)内部定义的类成为局部内部类。
局部类不能用public或者private访问修饰符修饰。它的作用域被限定在声明这个局部类的块中。局部内部类只在定义的局部中有效,就想定义的局部变量一样,在定义的方法体外不能创建局部内部类的对象。
1.方法定义局部内部类同方法定义局部变量一样,不能使用private、protected、public等访问修饰说明符修饰,也不能使用static修饰,但可以使用final和   abstract修饰
  2.方法中的内部类可以访问外部类成员。对于方法的参数和局部变量,必须有final修饰才可以访问。
3.static方法中定义的内部类可以访问外部类定义的static成员
public class Jubu {
 private int size=5,y=7;
 public Object makeInner(int localVar){
  final int finalLocalVar=localVar;
  //创建内部类,该类只在makeInner()方法有效,就像局部变量一样。在方法体外部不能创建MyInner类的对象
  class MyInner{
   int y=4;
   public String toString(){
    return "OuterSize:"+size+
        "\nfinalLocalVar"+" "+"this.y="+this.y;
   }
  }
  return new MyInner();
 }
}
class Main{ 
 public static void main(String[] args) {
  Object obj=new Jubu().makeInner(47);//创建Jubu对象obj,并调用它的makeInner()方法,该方法返回一个
  //该方法返回一个MyInner类型的的对象obj,然后调用其同toString方法。
  System.out.println(obj.toString());
  // TODO Auto-generated method stub
 }
} 

【3】匿名内部类
定义类的最终目的是创建一个类的实例,但是如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到与一起完成,或者说在定义类的同时就创建一个类
  以这种方法定义的没有名字的类成为匿名内部类。
   声明和构造匿名内部类的一般格式如下:
new SuperType(construction parameters){
	Inner class method and data
}

new InterfaceType(){
	method and data
}

由于构造器的名字必须和类名相同,而匿名类没有类名,所以匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。
public class NiMing {
 private int size=5;
 public Object makeInner(int localVar){
  final int finalLocalVar=localVar;
  return new Object(){
   //使用匿名内部类
   public String toString(){
    return "OuterSize="+size+"\nfinalLocalVar="+finalLocalVar;
   }
  };
 }
public static void main(String args[]){
   Object obj=new NiMing().makeInner(47);
   System.out.println(obj.toString());
 }
}

【4】静态内部类
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为static,以便取消产生的引用。在内部类不需要访问外围类对象的时候,应该使用静态内部类。只能访问外部类的static成员,不能直接访问外部类的实例变量,与实例方法,只有通过对象引用才能访问。由于static内部类不具有任何对外部类实例的引用,因此static内部类中不能使用this关键字来访问外部类中的实例成员,但是可以访问外部类中的static成员。
public class MyOuter {
 public static int x=100;
 public static class MyInner{
  private String y="Hello!";
  public void innerMethod(){
   System.out.println("x="+x);
   System.out.println("y="+y);
  }
 }
 public static void main(String[] args) {
  MyOuter.MyInner si=new MyOuter.MyInner();//静态内部类不通过外部实例就可以创建对象;与类变量可以通过类名访问相似
  si.innerMethod();
  // TODO Auto-generated method stub
 }
}


6.4代理
代理可以在运行的时候创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。
代理类具有下列方法:
指定接口所需要的全部方法。
Object类中的全部方法,例如:toString,equals等。
调用处理器是实现了InvocationHandler接口的类对象。调用代理对象的方法,调用处理器的invoke方法都会被调用,兵向其传递Method对象和原始的调用参数。要想代理一个代理对象,需要使用Proxy类的newProxyInstance方法。这个方法有三个参数:
(1) 类加载器,可以用null表示使用默认的类加载器
(2) Class对象数组,每个元素都是需要实现的接口。
(3) 调用处理器,实现了InvocationHandler的类对象。
class TraceHandler implements InvocationHandler{
	private Object target;
public TraceHandler(Object t){
		target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
	return m.invoke(target, args);
}
} 
Object value=. . . ;
TraceHandler handler=new TraceHandler(value);
Class[] interfaces = new Class[]{Comparable.class}
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

如果调用了代理对象的方法就会调用invoke方法。(500.compareTo(288))
代理类是在程序运行过程中创建的,一旦被创建,就变成了常规类。所有的代理类都扩展于Proxy类。一个代理类只有一个实例域——调用处理器,它定义在Proxy的超类中。
对于特定的类加载器和预设的一组接口来说,只有一个代理类,如果使用同一个类加载器和接口数组调用两次newProcyInstance方法的话,只能得到通一个类的两个对象,也可以利用getProxyClass方法获得这个类:Class proxyClass = Proxy.getProxyClass(null,interfaces);代理类一定是public和final。
static bollean isProxyClass(Class c)如果c是一个代理类返回true;

猜你喜欢

转载自beginner-dai.iteye.com/blog/2322798