黑马程序员-抽象类,接口和内部类

-----------android培训java培训、java学习型技术博客、期待与您交流! ------------


一、抽象类


1、抽象方法:


(1)由来:
多个类有相同的方法声明,但是方法体不一样。这个时候,我们考虑把方法声明进行抽取(即:只抽取功能定义,而不抽取功能主体)。让子类继承后,自己去实现方法体。
例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。
(2)声明:
没有方法体的方法,我们需要用声明一下。抽象的关键字是:abstract。它只能修饰类和方法,不能修饰变量
注意:抽象方法声明语句后要加分号。

2、抽象类:


父类中可以定义没有方法体的方法(抽象方法),该方法的具体实现由子类完成;包含抽象方法的类就是抽象类。

3、抽象类的特点:


1)抽象类中不一定有抽象方法;但是,有抽象方法的类一定是抽象类。
也就是说:抽象方法一定在抽象类中。
2)抽象类和抽象方法都要用abstract进行修饰
3)抽象类不能被实例化。即:抽象类不可以用new创建对象。因为调用抽象方法没意义。
4)抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类,不能创建对象。

4、抽象类典型问题:


(1)抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
答:抽象类有构造方法。
抽象类不能被实例化。
抽象类中的构造方法供子类实例化调用。
(2)abstract关键字和哪些关键字不能共存。
答: final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。
     而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。
    而抽象方法是没有方法体的,这样的调用无意义。

5、代码示例:


abstract class Student
{
	abstract  void study();//子类必须覆盖抽象方法。否则子类也是抽象类,也属于强制行为。,是抽象方法子类就必须复写
	void sleep()//能有非抽象方法
	{
		System.out.println("躺着");
	}
}
class BaseStudent extends Student
{
	void study()
	{
		System.out.println("base study");
	}
}
class AdvStudent extends Student
{
	void study()
	{
		System.out.println("adv study");
	}
}
class AbstractDemo 
{
	public static void main(String[] args) 
	{
		new BaseStudent().study();
		new BaseStudent().sleep();
		new AdvStudent().study();
	}
}

6、抽象类的作用:


(1)告诉编译器打算怎样来使用抽象类:即编译器会阻止产生这个类的任何对象,强迫建立子类实现功能;
(2)规定一些子类必须具有的功能,强迫子类复写其抽象方法;
(3)重构工具,使我们很容易的将公共方法沿着继承层次结构向上移动。

7、产生抽象类的原因:


通过某个通用接口建立起一种基本形式,以此表示所有导出类的共同部分,只表示一个接口,没有具体的实现内容;因此,创建一个实例对象没有什么意义,我们要阻止使用者这样做。


二、接口


1、接口出现的意义:

接口使抽象的概念更向前迈进了一步。interface这个关键字产生一个完全抽象的类,它根本没有提供任何具体实现。


2、接口的定义:

接口就是一组规范和要求,java接口是一些方法和特征的集合,但没有方法的实现,在java中接口定义的规范和要求是以抽象方法来实现。

接口被用来建立类与类之间的协议。


3、接口的成员特点:

   (1)成员变量
是常量,默认修饰 public static final
  (2)成员方法
都是抽象的,默认修饰 public abstract


4、关系

 

(1)类与类的关系(单继承)


是继承关系。类与类只能单继承,可以多重继承。


(2)类和接口的关系(多实现)


是实现关系。类可以多实现接口。
类在继承一个类的同时,可以实现多个接口。


(3)接口和接口的关系(多继承)


是继承关系。接口可以多继承接口。

代码示例:

interface A
{
	void methodA();
}
interface B //extends A
{
	void methodB();
}

interface C extends B,A//接口里面可以有多继承
{
	void methodC();
}

class D implements C
{
	public void methodA(){}
	public void methodC(){}
	public void methodB(){}
}


5、接口的特点:

(1)接口是对外 暴露的规则
(2)接口是程序的 功能扩展
(3)接口的出现 降低耦合性
耦合(类与类之间的关系)
内聚(类完成功能的能力)
编程规范:低耦合,高内聚。
(4)接口可以用来 多实现
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
(5)接口与接口之间可以有继承关系。

6、接口和抽象类的区别:

 

(1)接口的出现避免了无法多继承的局限性。


抽象类只能被单继承。一个类只能单继承一个类。
接口可以多实现。一个类可以多实现多个接口。


(2)数据特点


1)抽象类中的数据特点:
  成员变量:可以是变量,也可以是常量
  成员方法:可以是抽象方法,也可以是非抽象方法
  构造方法:有构造方法
2)接口中的数据特点:
  成员变量:是常量。默认修饰 public static final
  成员方法:都是抽象方法。都有默认修饰 public abstract
  构造方法:没有构造方法


(3)定义功能的种类

 

抽象类中定义的是继承体系中的共性功能。

接口中定义的是继承体系中的扩展功能。


(4)与子类的关系

 

抽象类被继承。是"is a"关系:xx是yy的一种,属于yy的体系

接口被实现。是"like a"关系:xx像yy,具备yy的特点,但不属于yy的体系


7、使用接口的原因:


(1)核心原因:为了能够向上转型为多个基类型,以及由此带来的灵活性。(高于抽象类的地方)

代码示例如下:
// Multiple interfaces.

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
	}
} // /:~

(2)防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。(和抽象类目的相同)

8、使用接口的情况:


(1)接口和抽象类:如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
(2)编程时思路:基本功能定义在类中,扩展功能定义在接口中。
(3)方法与接口:如果想要用某个方法操作不在继承结构中的某个类,这时用接口。(如果此时用类,那么该方法只能操作这个类及其子类)
(4)优先选择类,而不是接口,接口易被滥用。


三、内部类


1、定义:

将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。

2、访问特点:

(1)内部类可以直接访问外部类中的成员,因为内部类持有外部类的引用,
格式为:外部类名.this.x
(2)外部类要想访问内部类的成员,必须创建对象访问。
(3)要想生成内部类的对象,只能通过外部类的对象"new"出内部类的对象。

3、内部类的访问:


(1)外部类访问内部类


当内部类定义在外部类的成员位置,而且非私有,则可以在其他外部类中直接建立内部类对象
格式:外部类名.内部类名  变量名 = new 外部类对象().内部类对象()
如:Outer.Inner in = new Outer().new Inner()


(2)内部类的修饰符


当内部类在成员位置上,就可以被成员修饰符所修饰。比如:
private:将内部类在外部类中进行封装。只有内部类才能被private修饰。
Static:当内部类被static修饰后,内部类就具备static的特性。只能直接访问外部类中的static成员。出现了访问局限。


(3)静态内部类


当内部类在外部类的成员位置,且被static修饰时
a)外部其他类可直接访问静态内部类的非静态成员
格式:new 外部类名.内部类名().内部类成员
如:new Outer.Inner().function();
b)外部其他类可直接访问静态内部类的静态成员
格式:new 外部类名.内部类名.内部类成员
如:new Outer.Inner.function();


(4)注意:


当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。

静态内部类代码如下:
class Outer {
	private static int x = 3;

	static class Inner// 静态内部类
	{
		static void function() {
			System.out.println("innner :" + x);
		}
	}

	static class Inner2 {
		void show()// 不是静态方法必须通过new静态内部类然后调用方法
		{
			System.out.println("inner2 show");
		}
	}

	public static void method() {
		// Inner.function();
		new Inner2().show();
	}

}

class Demo {
	public static void main(String[] args) {
		// 静态内部类可以直接访问
		Outer.method();
		Outer.Inner.function();
	}
}
/*output
  inner2 show
  innner :3
*/


4、什么时候使用内部类:

(1)当描述事物时,事物的内部还有事物,该事物用内部类来描述
(2)内部事物在使用外部事物的内容
(3)假如有A类和B类,A类想直接访问B类的成员;B类访问A类成员的时候,需要创建A类对象进行访问,这个时候,就可以把A类定义为B类的内部类。

5、内部类的位置

 

(1)成员位置


1)可以被private修饰(Body,Heart)
2)可以被static修饰。(它访问的外部类的成员必须是静态的)
注意:只有定义在成员位置的内部类才能用private和static修饰


(2)局部位置(局部内部类)


1)可以直接访问外部类中的成员,因为还持有外部类的引用。
2)也可以直接访问局部成员,但是局部成员要用final修饰。  
注意:局部内部类不能用private和static修饰

内部类定义在局部时候:
/*
内部类定义在局部时,
1,不可以被成员修饰符修饰
2,可以直接访问外部类中的成员,因为还持有外部类中的引用。
	但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
 */
class Outer {
	int x = 3;

	void method(final int a) {
		final int y = 4;
		class Inner {
			void function() {
				System.out.println(y);// 访问局部变量需要是final所修饰的
				System.out.println(x);// 可以直接访问外部类变量
			}
		}

		new Inner().function();

	}
}

class Demo {
	public static void main(String[] args) {
		Outer out = new Outer();
		out.method(7);
		out.method(8);
	}

}

/*output:
4
3
4
3
*/

练习1:

/* Write a class named Outer that contains an inner class named Innet. 
 * Add a method to Outer that returns an object of type Inner. In main(),
 * create and initialize a reference to an Inner.
 */

public class Outer1 {
	class Inner {
		Inner() {
			System.out.println("Inner()");
		}
	}

	Outer1() {
		System.out.println("Outer1()");
	}

	// make an Inner from within a non-static method of outer class:
	Inner makeInner() {
		return new Inner();
	}

	public static void main(String[] args) {
		Outer1 o = new Outer1();
		Inner i = o.makeInner();
	}
}

/*output:
Outer1()
Inner()
*/

练习2:

/* Modify Exercise 1 so that Outer has a private String field (initialized
 * by the constructor), and Inner has a toString() that displays this field.
 * Create an object of type Inner and display it.
 */

public class Outer2 {
	private String s;

	class Inner2 {
		Inner2() {
			System.out.println("Inner()");
		}

		public String toString() {
			return s;
		}
	}

	Outer2(String s) {
		System.out.println("Outer1()");
		this.s = s;
	}

	Inner2 makeInner3() {
		return new Inner2();
	}

	public static void main(String[] args) {
		Outer2 o = new Outer2("Hi is risen!");
		Inner2 i = o.makeInner3();
		System.out.println(i.toString());
	}
}

/*output:
Outer1()
Inner()
Hi is risen!
*/


四、匿名内部类


1、前提:

继承一个类或者实现一个接口(注意不要弄混匿名内部类的前提和多态的前提)。


2、格式:

new 父类名或者接口名()
{
重写父类方法或者实现接口中的方法。
也可以自定义其他方法。
};


3、实质:

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


代码示例:

// Returning an instance of an anonymous inner class.

public class Parcel {
	public Contents contents() {
		return new Contents() { // Insert a class definition
			private int i = 11;

			public int value() {
				return i;
			}
		}; // Semicolon required in this case
	}

	public static void main(String[] args) {
		Parcel p = new Parcel();
		Contents c = p.contents();
	}
} // /:~

contents()方法将返回值的生成与描述这个返回值的类的定义结合在一起!

匿名内部类的语法指的是:“创建一个继承自Contents的匿名类的对象。”通过new表达式返回的引用被自动向上转型为对Contents的引用。

上述程序其实是下述代码的简化形式:

// Expanded version of Parcel.java

public class Parcel2 {
	class MyContents implements Contents {
		private int i = 11;

		public int value() {
			return i;
		}
	}

	public Contents contents() {
		return new MyContents();
	}

	public static void main(String[] args) {
		Parcel2 p = new Parcel2();
		Contents c = p.contents();
	}
} // /:~



4、何时定义匿名内部类:

匿名内部类只是为了简化书写,匿名内部类有局限,通常定义匿名内部类时,该类方法不超过3个。


5、匿名内部类的好处和弊端:


(1)好处:

简化代码书写

(2)弊端:

 1)不能直接调用自己的特有方法
 2)不能执行强转换动作
 3)如果该类里面方法较多,不允许使用匿名内部类
 


6、匿名内部类的使用规则:

一般只复写父类的功能,不定义新的功能,因为用父类引用无法调用新功能

 

7、使用局部内部类而不使用匿名内部类的情况:

(1)需要一个已命名的构造器,或者需要重载构造器时,用局部内部类;而匿名内部类只能用于实例化。

(2)需要不止一个该内部类的对象,用局部内部类。

匿名内部类代码示例:

 

abstract class AbsDemo {
	abstract void show();

}

class Outer {
	public void function() {
		AbsDemo d = new AbsDemo() {
			int num = 9;

			void show() {
				System.out.println("num===" + num);
			}

			void abc() {
				System.out.println("haha");
			}
		};// 这就是匿名内部类

		d.show();
		// d.abc();//编译失败;多态中编译看左边,父类中没有abc()方法所以编译失败
	}
}

class Demo {
	public static void main(String[] args) {
		new Outer().function();
	}
}

/*output:
num===9
*/

练习:

interface Content {
	int value();
}

class Anonymous {
	public Content contents() {
		return new Content() {
			private int i = 11;

			public int value() {
				return i;
			}
		};
	}

	public static void main(String[] args) {
		Anonymous a = new Anonymous();
		Content c = a.contents();
		int v = c.value();
		System.out.println(v);
	}
}

/*output:
11
*/


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值