第九天 Java面向对象之抽象类、接口、内部类、代码块

一、抽象类

1. 概念

  1. 用abstract关键字来修饰一个类,这个类叫做抽象类。  用abstract来修饰一个方法,该方法叫做抽象方法。
  2. 抽象方法:只有方法的声明,没有方法的实现。以分号结束:
    比如:public abstract void talk();
  3. 含有抽象方法的类必须被声明为抽象类。
  4. 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
  5. 不能用abstract修饰变量、代码块、构造器;
  6. 不能用abstract修饰私有方法、静态方法、final的方法、final的类。

2. 注意事项

(1)抽象类中可以有成员变量、成员方法以及构造方法。
(2)抽象类中可以没有抽象方法,也可以有抽象方法。
(3)拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类是由abstract关键字
修饰并且拥有抽象方法的类。

3. 实例应用

  • 在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。
  • 问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。
  • 解决方案
    Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。
    这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
  • Vehicle是一个抽象类,有两个抽象方法。
public abstract class Vehicle{
	public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法
	public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}

public class Truck extends Vehicle{
	public double calcFuelEfficiency( ) { //写出计算卡车的燃料效率的具体方法 }
	public double calcTripDistance( ) { //写出计算卡车行驶距离的具体方法 } }
	
public class RiverBarge extends Vehicle{
	public double calcFuelEfficiency( ) { //写出计算驳船的燃料效率的具体方法 }
	public double calcTripDistance( ) { //写出计算驳船行驶距离的具体方法} }

注意:抽象类不能实例化 new Vihicle()是非法的

4. 实际意义

抽象类的实际意义不在于自身创建对象而在于被继承,当一个类继承抽象类之后必须重写抽象方法,否则该类也变成抽象类。
因此抽象类对子类具有强制性和规范性,叫做模板设计模式。

  • 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模
    板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象
    类的行为方式。

解决的问题:

  • 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

二、接口 (interface)

1. 基本概念

接口主要指比抽象类还抽象的类,因此不能实例化对象。
接口是抽象方法和常量值定义的集合。
定义类的关键字使用class,而定义接口使用interface关键字。

2.特点

  • 用interface来定义。
  • 接口中的所有成员变量都默认是由public static final修饰的。
  • 接口中的所有抽象方法都默认是由public abstract修饰的。
  • 接口中没有构造器。
  • 接口采用多继承机制。

3. 类和接口之间的关系

类和类之间的关系 使用extends关键字表达继承的关系 支持单继承
类和接口之间的关系 使用implements关键字表达实现的关系 支持多实现
接口和接口之间的关系 使用extends关键字表达继承的关系 支持多继承

4. 抽象类和接口之间的主要区别

  1. 定义抽象类的关键字是abstract class,而定义接口的关键字是interface。
  2. 继承抽象类的关键字是extends,而实现接口的关键字是implements。
  3. 继承抽象类支持单继承,而实现接口支持多实现。
  4. 抽象类中可以有构造方法,而接口中不可以有构造方法。
  5. 抽象类中可以有成员变量,而接口中只可以有常量。
  6. 抽象类中可以有成员方法,而接口中只可以有抽象方法。
  7. 抽象类中增加方法可以不影响子类,而接口中增加方法通常都会影响实现类。
  8. 从jdk1.8开始允许接口中有非抽象方法,但必须使用default关键字修饰。

在这里插入图片描述

5. 实例

  • 定义Java类的语法格式:先写extends,后写implements
    class SubClass extends SuperClass implements InterfaceA{ }
  • 一个类可以实现多个接口,接口也可以继承其它接口。
  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  • 接口的主要用途就是被实现类实现。(面向接口编程)
  • 与继承关系类似,接口与实现类之间存在多态性
  • 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。

在这里插入图片描述
在这里插入图片描述

 一个类可以实现多个无关的接口
interface Runner { public void run();}
interface Swimmer {public double swim();}
class Creator{public int eat(){}} 
class Man extends Creator implements Runner ,Swimmer{
	public void run() {……}
	public double swim() {……}
	public int eat() {……}
}
与继承关系类似,接口与实现类之间存在多态性
public class Test{
	public static void main(String args[]){
		Test t = new Test();
		Man m = new Man();
		t.m1(m);
		t.m2(m);
		t.m3(m);
}
	public String m1(Runner f) { f.run(); }
	public void m2(Swimmer s) {s.swim();}
	public void m3(Creator a) {a.eat();}
}

6. jdk1.8 后的改进

  1. Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完
    全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
  2. 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行
    其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中
    找到像Collection/Collections或者Path/Paths这样成对的接口和类。
  3. 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
    我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
    比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认
    方法。

7. 接口默认方法

  1. 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同
    参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接
    口时,会出现:接口冲突。
    解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
  2. 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非
    抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有
    相同名称和参数的默认方法会被忽略。
interface Filial {// 孝顺的
	default void help() {
	System.out.println("老妈,我来救你了");
} }
interface Spoony {// 痴情的
	default void help() {
	System.out.println("媳妇,别怕,我来了");
} }

class Man implements Filial, Spoony { @Override
public void help() {
System.out.println("我该怎么办呢?");
Filial.super.help();"来解决冲突"
Spoony.super.help();"解决冲突"
} }

三、内部类

1.概念

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
  • 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
  • Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
    Inner class的名字不能与包含它的外部类类名相同;
  • 分类:
    成员内部类(static成员内部类和非static成员内部类)
    局部内部类(不谈修饰符)、匿名内部类
为什么需要内部类?

java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就行了吗?
的确,以此作为设计内部类的理由,实在没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题 没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果

2. 声明

class 外部类{

	修饰符 class 类名 {
		//成员内部类;
	}
	方法(){
		class 局部内部类{
		 }
	 }
	 {
		class 局部内部类{
		 }
	 } 
 }
1. 成员内部类

1. 成员内部类作为类的成员的角色:

  • 和外部类不同,Inner class还可以声明为private或protected;
  • 可以调用外部类的结构
  • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;

2. 成员内部类作为类的角色:

  • 可以在内部定义属性、方法、构造器等结构
  • 可以声明为abstract类 ,因此可以被其它的内部类继承
  • 可以声明为final的
  • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

【注意】

  1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
  2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
    Outer a = new Outer();//声明外部类
    Outer.Inner b = a.new Inner();//声明内部类
    Outer.Inner o=new Outer().new Inner();
  3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据
  4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
public class Outer {//外部类
	private int s = 111;
	public class Inner {//内部类
		private int s = 222;
		public void mb(int s) {
			System.out.println(s); // 局部变量s
			System.out.println(this.s); // 内部类对象的属性s
			System.out.println(Outer.this.s); // 外部类对象属性s 
		}
   }
 	public static void main(String args[]) {
		Outer a = new Outer();
		Outer.Inner b = a.new Inner();
		b.mb(333);
	}
 }
2. 局部内部类
1)局部内部类的特点
  1. 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
  2. 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
  3. 局部内部类可以使用外部类的成员,包括私有的。  局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
  4. 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
  5. 局部内部类不能使用static修饰,因此也不能包含静态成员
2)如何使用局部内部类
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
  • 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
class 外部类{

	方法(){
		class 局部内部类{
		 }
	 }
	 {
		class 局部内部类{
		 }
	 } 
 }
3. 匿名内部类

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

格式:
接口/父类类型 引用变量名 = new 接口/父类类型(){ 方法的重写 };

new 父类构造器(实参列表)|实现接口(){
	//匿名内部类的类体部分
}

匿名内部类的特点

  • 匿名内部类必须继承父类或实现接口
  • 匿名内部类只能有一个对象
  • 匿名内部类对象只能使用多态形式引用
interface A{
	public abstract void fun1();
}

public class Outer{
	public static void main(String[] args) {
		new Outer().callInner(new A(){
		//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
		public void fun1() {
			System.out.println(“implement for fun1");
			}
		});// 两步写成一步了
	}
	
	public void callInner(A a) {
		a.fun1();
	}
}

四、代码块

代码块(或初始化块)的作用

  • 对Java类或对象进行初始化

代码块(或初始化块)的分类:

  • 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块(static block),没有使用static修饰的,为非静态代码块。
  • static代码块通常用于初始化static的属性
class Person {
	public static int total;
	static {
		total = 100;//为total赋初值
	}
	…… //其它属性或方法声明
}

静态代码块:用static 修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行一次。

非静态代码块:没有static修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 除了调用非静态的结构外,还可以调用静态的变量或方法。
  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  5. 每次创建对象的时候,都会执行一次。且先于构造器执行。

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值