面向对象基础四

面向对象基础四

#抽象类

在实际设计中,有这样的需要,就是某个基类不允许创建对象。我们可以在定义该类时,在前面加上 abstract 关键字,该类不能被实例化,如果要创建抽象类的对象,编译器会报错。

抽象类也有构造方法,因为其子类会调用父类的构造方法,该构造方法在子类创建对象时被调用。

#抽象方法

在定义方法时,如果在前面加上abstract关键字,这时该方法没有方法体,这种方法称为抽象方法。 例如:

public abstract void m1();

抽象方法只能出现在抽象类中,子类继承父类时,如果子类被定义为非抽象类,则在子类中必须实现父类继承来的抽象方法,除非它也被定义为抽象类。

抽象类体中,可以包含抽象方法,也可以不包含抽象方法。但类体中包含抽象方法的类必须要声明为抽象类。

不管是抽象类还是抽象方法都不能被final修饰,这是因为final类不能被继承,final方法不能被复写,而抽象类需要被继承,抽象方法需要被复写,很显然这两者是冲突的。

#接口(interface)

接口也是一类引用类型,interface 比抽象类更进一步,在interface中所有的方法均不能有方法体。interface 非常有用,因为在Java中类不允许多重继承,但是某个类可以实现多个interface,接口和接口之间可以多继承。

#接口的特点

  • 在抽象的类中,并非所有的方法都是抽象的,那些抽象的方法在子类中实现。而在interface中所有的方法都是抽象的,不能有方法体。
  • 接口中没有构造方法,无法被实例化。
  • 接口中的成员变量和方法的存取控制权限必须是public,所以存取控制符修饰符可以省略,就是指public
  • 接口中也可以有成员变量,但是这些成员变量必须是 staticfinal,在接口中要给域初始化,这两个关键字可以省略。
  • 接口中只能出现:常量、方法

#接口的定义与实现

interface取代class关键字就是定义接口的形式。如果interface需要公开,则interface 前也可以有public,因此也要求文件名也必须和接口名一致。

一个类可以实现多个接口,使用关键字 implements 后跟若干接口名,接口名之间用逗号分隔。实现接口的类除非定义为抽象类,否则,一个非抽象的类在实现接口时,需要将接口中所有的方法进行实现。

因为接口中的方法如果没有存取控制符修饰的话,就是public,所以在实现接口的类中,应在方法前加上存取控制符 public

如下例:

interface A{
  	int i=0;
  	void m();
}

class B implements A{
  	//int i=9;
  	public void m(){}
  	
	public static void main(String s[]){
    	B x=new B();
    	System.out.println(x.i);
  	}
}

再如:

interface CanFight {
	void fight();
}

interface CanSwim {
	void swim();
}

interface CanFly {
	void fly();
}

class ActionCharacter {
	public void fight() {}
}

class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
	public void swim() {}
	public void fly() {}
}

public class Adventure {
	public static void t(CanFight x) { x.fight(); }
	public static void u(CanSwim x) { x.swim(); }
	public static void v(CanFly x) { x.fly(); }
	public static void w(ActionCharacter x) { x.fight(); }

	public static void main(String[] args) {
		Hero h = new Hero();
		t(h); // Treat it as a CanFight
		u(h); // Treat it as a CanSwim
		v(h); // Treat it as a CanFly
		w(h); // Treat it as an ActionCharacter
	}
}

注意在接口CanFight 和类ActionCharacterfight( )一样,所以在Hero中没有提供定义。

#接口和抽象类的区别

  • 相同点
    • 都包含抽象方法
    • 都不能被实例化
    • 都是引用数据类型。可以声明抽象类及接口变量,并将子类的对象赋给抽象类变量,或将实现接口的类的变量赋给接口变量
  • 不同点
    • 只能继承一个抽象类,但可以实现多个接口,接口可以多继承
    • 抽象类的访问权限控制同普通类,接口的访问权限只有public即是默认权限,所以public可以省略
    • 接口中只能声明常量,不能声明变量
    • 接口中只能声明抽象方法
  • 使用接口的好处
    • 可以使项目分层,所有层都面向接口开发,开发效率提高了。
    • 接口使代码和代码之间的耦合度降低,就像内存条和主板的关系,变得“可插拔”。可以随意切换。
    • 接口和抽象类都能完成某个功能,优先选择接口。

例子:

public interface CustomerService{
	
	//定义一个退出系统的方法
	void logout();
	
}

public class CustomerServiceImpl implements CustomerService{

	//对接口中的抽象方法进行实现。
	public void logout(){
		
		System.out.println("成功退出系统!");
		
	}
	
}

public class Test{

	//入口
	public static void main(String[] args){
		
		//要执行CustomerServiceImpl中的logout方法.
		
		//以下程序面向接口去调用	
		CustomerService cs = new CustomerServiceImpl(); //多态
		
		//调用
		cs.logout();
	}
}

#内部类

内部类就是在一个类中所定义的类,它可以在一个类中定义,也可以在一个方法中定义,如下:

class EnclosingClass {
 	... 
  	static class StaticNestedClass { ... }
  	class InnerClass { ... } 
}
  • 内部类像类的其他成员一样,有自己的作用域,且内部类的名称不能与所嵌套的类重名。
  • 内部类可以是由static修饰。
  • 内部类也可以由abstract修饰,该内部类不能被实例化。
  • 因为内部类在一个类的内部被定义,则修饰它的的访问控制修饰符可以是private、protected、public、缺省等权限。
  • 非静态内部类的成员不能声明为static类型
  • 使用内部类也相当于变相的实现了多重继承
//: Parcel2.java
// Returning a reference to an inner class.
public class Parcel{
  	class Contents {
    	private int i = 11;
    	public int value() { return i; }
  	}

  	class Destination {
    	private String label;

    	Destination(String whereTo) {
      		label = whereTo;
    	}

    	String readLabel() { return label; }
  	}

  	public Destination to(String s) {
    	return new Destination(s);
  	}

  	public Contents cont() {
    	return new Contents();
  	}

  	public void ship(String dest) {
    	Contents c = cont();
    	Destination d = to(dest);
    	System.out.println(d.readLabel());
  	}

  	public static void main(String[] args) {
    	Parcel p = new Parcel2();
    	p.ship("Tanzania");
    	Parcel q = new Parcel2();
    
		// Defining references to inner classes:
    	Parcel.Contents c = q.cont();
		// Parcel2.Contents c = p.new Contents();
		//如果Contents为static的话,可以new Parcel2.Contents();
    	Parcel.Destination d = q.to("Borneo");
  	}
}

#非静态内部类

非静态内部类可以等同看做实例变量 非静态内部类中不能有静态声明. 非静态内部类可以访问外部类所有的数据.

#静态内部类

静态内部类可以等同看做静态变量 静态内部类可以直接访问外部类的静态数据,无法直接访问非静态成员。 内部类重要的作用:可以访问外部类中私有的数据。

#局部内部类

局部内部类等同于局部变量,它定义在方法内部,可出现在形式参数定义处或者方法体处,不能使用访问控制符,不能使用static。局部内部类可以访问方法中的局部变量,但该局部变量必须是使用final修饰的。这是为什么呢? 这是因为:

  1. JAVA语言的编译程序的设计者当然希望能无条件访问局部变量,从理论上这是很自然的要求,但是,编译技术是无法实现的或实现代价极高。
  1. 困难在何处?到底难在哪儿?局部变量的生命周期与局部内部类的对象的生命周期的不一致性!
  2. 设方法f()被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i。当方法f()运行结束后,局部变量i就已死亡了,不存在了。但局部内部类对象inner_object还可能一直存在(在没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡。这时:出现了一个"荒唐"结果:局部内部类对象inner_object要访问一个已不存在的局部变量i!
  3. 如何才能实现?当变量是final时,通过将final局部变量复制一份,复制品直接作为局部内部中的数据成员。这样,当局部内部类访问局部变量时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量)。因此,当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以访问局部变量(其实访问的是复制品),给人的感觉:好像是局部变量的生命期延长了。
  4. 那么,核心的问题是,怎么才能使得访问复制品与访问真正的原始的局部变量,其语义效果是一样的呢? 当变量是final时,若是基本数据类型,由于其值不变,因而,其复制品与原始的量是一样。语义效果相同。(若:不是final,就无法保证复制品与原始变量保持一致了。因为,在方法中改的是原始变量,而局部内部类中改的是复制品)
  5. 当变量是final时,若是引用类型,由于其引用值不变(即:永远指向同一个对象)。因而,其复制品与原始的引用变量一样,永远指向同一个对象(由于是final,从而保证:只能指向这个对象,再不能指向其它对象)。达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个,即:语义效果是一样的。否则:当方法中改变原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量。)
public class OuterClass{
	//方法
	public void m1(){
		//局部变量
		final int i = 10;
		//局部内部类
		//局部内部类不能用访问控制权限修饰符修饰。
		class InnerClass{
			//内部类不能有静态声明
			//public static void m1(){}
			//成员方法
			public void m2(){
				System.out.println(i); // 10
			}
		}
		//调用m2
		InnerClass inner = new InnerClass();
		inner.m2();
	}
	//入口
	public static void main(String[] args){
		OuterClass oc = new OuterClass();
		oc.m1();
	}
}

#匿名内部类

匿名内部类:指的是类没有名字。 由于匿名内部类没有类名,因此匿名内部类不能显式声明构造方法。 匿名内部类在定义时即被实例化,所以匿名内部类不能定义为抽象类。

/*
	匿名内部类:指的是类没有名字.
*/
public class Test{
	
	//静态方法
	public static void t(CustomerService cs){
		cs.logout();
	}
	//入口
	public static void main(String[] args){
		//使用匿名内部类的方式执行t方法
		//整个这个"new CustomerService(){}"就是个匿名内部类
		t(new CustomerService(){
			public void logout(){
				System.out.println("exit!");
			}
		});
		//匿名内部类的优点:少定义一个类.
		//缺点:无法重复使用!
	}
}
//接口
interface CustomerService{
	//退出系统
	void logout();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值