8)多态 抽象类 接口

多态的概述

  • A:多态(polymorphic)概述
    • 事物存在的多种形态
  • B:多态前提
    • a:要有继承关系。
    • b:要有方法重写。
    • c:要有父类引用指向子类对象。
      fu a=new zi();

多态中的成员访问特点之成员变量

  • 成员变量

    • 编译看左边(父类),运行看左边(父类)。
  • 成员方法

    • 编译看左边(父类),运行看右边(子类)。
  • 静态方法

    • 编译看左边(父类),运行看左边(父类)。
    • (静态和类相关,算不上重写,所以,访问还是左边的)
    • 只有非静态的成员方法,编译看左边,运行看右边
      f.method() 相当于类名.方法

多态中向上转型和向下转型

  • Person p = new SuperMan();向上转型
    SuperMan sm = (SuperMan)p;向下转型

多态的好处和弊端

  • A:多态的好处
    • a:提高了代码的维护性(继承保证)
    • b:提高了代码的扩展性(由多态保证)
  • B
    • 多态的好处
    • 可以当作形式参数,可以接收任意子类对象
  • C:多态的弊端
    • 不能使用子类的特有属性和行为。
      instanceof 判断前边引用是否是后面的数据类型
    if(staff[i] instanceof Manager)
 {
     boss=(Manager)staff[i];
 }

动态绑定和静态绑定

  • 动态绑定
  1. x.f(param) 隐示参数x为c类 编译器列举出所有c类f名字的方法和超类属性为public名为f的方法
    2)找参数类型符合的方法 这个过程叫重载解析 允许类型转换int->double manager->employee 编译器没找到 报错
    子类返回值在覆盖方法时一定要是原返回类型的子类型
  • 静态绑定
    private static final 构造器

阻止继承 final类和方法

final类中方法自动变为final方法 不能被继承 final方法不能被覆盖

抽象类的概述及其特点

  • 抽象类特点
    • a:抽象类和抽象方法必须用abstract关键字修饰
      • abstract class 类名 {}
      • public abstract void eat();
    • b:抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者是接口
    • c:抽象类不能实例化那么,抽象类如何实例化呢?
      • 按照多态的方式,由具体的子类(继承了抽象类)实例化。其实这也是多态的一种,抽象类多态。
    • d:抽象类的子类
      • 要么是抽象类
      • 要么重写抽象类中的所有抽象方法

抽象类的成员特点

  • A:抽象类的成员特点
    • a:成员变量:既可以是变量,也可以是常量。abstract是否可以修饰成员变量?不能修饰成员变量
    • b:构造方法:有。
      • 用于子类访问父类数据的初始化。
    • c:成员方法:既可以是抽象的,也可以是非抽象的。
  • C:抽象类的成员方法特性:
    • a:抽象方法 强制要求子类做的事情。
    • b:非抽象方法 子类继承的事情,提高代码复用性。
    • 面向对象(抽象类中的面试题)

  • A:面试题1
    • 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
    • 可以
    • 这么做目的只有一个,就是不让其他类创建本类对象,交给子类完成
  • B:面试题2
    • abstract不能和哪些关键字共存
      被abstract修饰的方法没有方法体 static修饰的方法可以用类名.方法调用 无意义
      final修饰的子类不能重写 abstract修饰强制子类重写
      private 不让子类访问
      abstract 让子类访问
package abstractClasses;

public abstract class Person {
	public abstract String getDescription();
	private String name;
	
	public Person(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}
	
	

}
package abstractClasses;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee extends Person
{
	private double salary;
	private Date  hireDay;
	
	public Employee(String name, double salary, int year,int month,int day) {
		super(name);
		this.salary = salary;
		GregorianCalendar calendar=new GregorianCalendar(year,month-1,day);
		//0代表一月
		hireDay=calendar.getTime();
	}

	public double getSalary() {
		return salary;
	}

	public Date getHireDay() {
		return (Date) hireDay.clone();
	}

    public String getDescription()
    {
    	return String.format("an employee with a salary of $%.2f",salary);
    }
    
	public void raiseSalary(double byPercent)
	{
		double raise=salary*byPercent/100;
		salary+=raise;
	}
}
package abstractClasses;

public class Student extends Person{

	private String major ;

	public Student(String name,String major) {
		super(name);
		this.major = major;
		
		
	}

	@Override
	public String getDescription() {
		return "a student major in "+ major;
	}
	
}

package abstractClasses;



public class PersonTest {

	public static void main(String[] args) {
		Person[] people = new Person[2];
		//抽象类不能实例化 但可以定义变量引用对象
		people[0] = new Employee("Harry Hacker", 50000, 1989,10,1);
		people[1] = new Student("Maria Morris", "computer science");
		
		for (Person p : people) {
			System.out.println(p.getName()+","+p.getDescription());
		}//可以用p调用方法  不抽象 各自写get方法 不能用p调用
	}

}

接口的概述及其特点

  • A:接口概述
    • 从狭义的角度讲就是指java中的interface
    • 从广义的角度讲对外提供规则的都是接口
  • B:接口特点
    • a:接口用关键字interface表示
      • interface 接口名 {}
    • b:类实现接口用implements表示
      • class 类名 implements 接口名 {}
    • c:接口不能实例化
      • 那么,接口如何实例化呢?
      • 按照多态的方式来实例化。
    • d:接口的子类
      • a:可以是抽象类。但是意义不大。
      • b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)

抽象类和接口的区别

  • A:成员区别

    • 抽象类:
      • 成员变量:可以变量,也可以常量
      • 构造方法:有
      • 成员方法:可以抽象,也可以非抽象
    • 接口:
      • 成员变量:只可以常量
      • 成员方法:只可以抽象
  • B:关系区别

    • 类与类
      • 继承,单继承
    • 类与接口
      • 实现,单实现,多实现
    • 接口与接口
      • 继承,单继承,多继承
  • C:设计理念区别

    • 抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
    • 接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。有的类有 有的类没有

泛型数组列表

package haha;

import java.util.ArrayList;

public class ArrayListTest {
//为了动态分配数组大小
	public static void main(String[] args) {
	//ArrayList<Employee> staff = new ArrayList<>(100); 分配空间 只是拥有100个的潜力 不含任何元素
		ArrayList<Employee> staff = new ArrayList<>();
		staff.add(new Employee("Carl Cracker", 75000, 1987,12,15));
		staff.add(new Employee("Harry Hacker", 50000, 1989,10,1));
		staff.add(new Employee("Tony Tester", 40000, 1990,3,15));
	//	staff.ensureCapacity(100); 分配空间
	//	System.out.println(staff.size()); 3
	//	staff.trimToSize(); 一旦确认不会再加
	//  staff.set(i,harry);
	//   staff.get(i)
	//	staff.add(n,e) 指定位置
     //   staff.remove(n)
		for (Employee e : staff) {
			e.raiseSalary(5);
		}
		for (Employee e : staff) {
			System.out.println("name="+e.getName()+",salary="+e.getSalary()+",hireday="+e.getHireDay());
			
		}
	}
             /*ArrayList<X> list = new ArrayList<>();
             while(...)
             {
            	 x = ...;
            	 list.add(x);
             }
             
             X[] a = new X[list.size()];
             list.toArray(a); 拷贝到数组*/
             
             
}

接口

  • 接口所有方法都为public不写也是
  • 接口决不能含有实例域和静态方法
  • 接口可以有变量,但一定是public static final 修饰,就算你不写,JVM也会默认是这个。
  • 一个接口不能继承其他类,但是可以继承别的接口。
package interfaces;

public class Employee implements Comparable <Employee> {
	private String name;
	private double salary;
	
	public Employee(String name, double salary) {
		super();
		this.name = name;
		this.salary = salary;
	}
	

	public String getName() {
		return name;
	}


	public double getSalary() {
		return salary;
	}
	
	public void raiseSalary(double byPersent)
	{
		double raise =salary*byPersent/100;
		salary+=raise;
	}
	//比较 工资  小于负数 等于0 大于正数
	@Override
	public int compareTo(Employee o) {
		return Double.compare(salary, o.salary);
	}

}

package interfaces;

import java.util.Arrays;

public class EmployeeSortTest {

	public static void main(String[] args) {
	 Employee[] staff = new Employee[3];
	 
	 staff[0] = new Employee("Harry Hacker",35000);
	 staff[1] = new Employee("Carl Cracler",75000);
	 staff[2] = new Employee("Tony Tester",38000);
	 
	 Arrays.sort(staff);
	 
	 for(Employee e:staff)
	 {
		 System.out.println("name="+e.getName()+",salary="+e.getSalary());
	 }
	 	/*	name=Harry Hacker,salary=35000.0
			 name=Tony Tester,salary=38000.0
			 name=Carl Cracler,salary=75000.0*/
	}
        //     为什么不在雇员类里直接写compareTo方法 而必须实现comparable方法呢
        //    java强类型语言 调用方法 编译器检查方法是否存在
}

  • 接口不是类 不能new 但是可以声明接口变量
  • Comparable x; x=new Employee();
  • 接口变量必须引用实现了接口的对象
  • 可以用instanceOf检查一个类是否实现了某个特定的 接口
  • 与抽象类相比 可以实现多个接口 但只能继承一个类

克隆

class CloneClass implements Cloneable{ 
 public int aInt; 
 public Object clone(){ 
  CloneClass o = null; 
  try{ 
   o = (CloneClass)super.clone(); 
  }catch(CloneNotSupportedException e){ 
   e.printStackTrace(); 
  } 
  return o; 
 }
  • 希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang 包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。
  • 重载了clone()方 法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或 间接调用了java.lang.Object类的clone()方法。
  • 观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为 什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能
  • 也要 观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类
  • 重写clone()方法。还有一点要考虑的是为了让其它类能调用这个clone 类的clone()方法,重写之后要把clone()方法的属性设置为public
  • 如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了 super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。

浅拷贝与深拷贝

  • 浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
  • 对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
  • 若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝,
  • 如果数据域是基本数据类型没有问题 要是有对子类对象引用,会引用同一个子类对象
  • 浅拷贝后果:如果是不可变的 没影响 String Date,或者没有更改的方法。
    1)默认克隆方法能否满足要求
    2)默认克隆方法是否能通过调用可变子类对象修补
    3)是否不应该使用clone
class Professor0 implements Cloneable {
    String name;
    int age;
 
    Professor0(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student0 implements Cloneable {
    String name;// 常量对象。
    int age;
    Professor0 p;// 学生1和学生2的引用值都是一样的。
 
    Student0(String name, int age, Professor0 p) {
        this.name = name;
        this.age = age;
        this.p = p;
    }
 
    public Object clone() {
        Student0 o = null;
        try {
            o = (Student0) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
 
        return o;
    }
}
public class ShallowCopy {
    public static void main(String[] args) {
        Professor0 p = new Professor0("wangwu", 50);
        Student0 s1 = new Student0("zhangsan", 18, p);
        Student0 s2 = (Student0) s1.clone();
        s2.p.name = "lisi";
        s2.p.age = 30;
        s2.name = "z";
        s2.age = 45;
        System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age);// 学生1的教授
    }
}
  • 浅拷贝
class Professor implements Cloneable {
    String name;
    int age;
 
    Professor(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }
}
class Student implements Cloneable {
    String name;
    int age;
    Professor p;
 
    Student(String name, int age, Professor p) {
        this.name = name;
        this.age = age;
        this.p = p;
    }
 
    public Object clone() {
        Student o = null;
        try {
            o = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        o.p = (Professor) p.clone();
        return o;
    }
}
public class DeepCopy {
    public static void main(String args[]) {
        long t1 = System.currentTimeMillis();
        Professor p = new Professor("wangwu", 50);
        Student s1 = new Student("zhangsan", 18, p);
        Student s2 = (Student) s1.clone();
        s2.p.name = "lisi";
        s2.p.age = 30;
        System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age);// 学生1的教授不改变。
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1);
    }
}
package clone;

import java.util.Date;
import java.util.GregorianCalendar;

public class Employee implements Cloneable {
	private String name;
	private  double salary;
	private  Date   hireDay;
	public Employee(String name, double salary) {
	
		this.name = name;
		this.salary = salary;
		hireDay = new Date();
	}
	public Employee clone() throws CloneNotSupportedException
	{
		Employee cloned = (Employee)super.clone();
		cloned.hireDay = (Date)hireDay.clone();
		return cloned;
	}
	public void setHireDay(int year,int month,int day) {
		Date newHireDay = new GregorianCalendar(year,month-1,day).getTime();
		
		hireDay.setTime(newHireDay.getTime());
	}
	public void raiseSalary(double byPercent)
	{
		double raise =salary *byPercent/100;
		salary +=raise;
		
	}
	
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
	}
	/*original = Employee [name=John Q. Public, salary=50000.0, hireDay=Sat Jan 01 00:00:00 CST 2000]
	copy = Employee [name=niubi, salary=55000.0, hireDay=Mon Dec 31 00:00:00 CST 2001]*/

	
}

package clone;

public class CloneTest {

	public static void main(String[] args) {
		try {
			Employee original = new Employee("John Q. Public", 50000);
			original.setHireDay(2000, 1, 1);
			Employee copy;
			copy = original.clone();
			copy.setName("niubi");
			copy.raiseSalary(10);
			copy.setHireDay(2001, 12, 31);
			System.out.println("original = "+original);
			System.out.println("copy = "+copy);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	
	}

}

问题:==与equals()的区别:
==:比较引用类型比较的是地址值是否相同
1.equals:比较引用类型默认也是比较地址值是否相同,注意:String类重写了equals()方法,比较的是内容是否相同。
2.String A = “ABC”;内存会去查找永久代(常量池) ,如果没有的话,在永久代中中开辟一块儿内存空间,把地址付给栈指针,如果已经有了"ABC"的内存,直接把地址赋给栈指针;

因此

String str1=“aa”;

Srting str2=“aa”;

String Str3=“aa”;

这样下去,str1Str2str3;会一直相等下去,(a) ==的判断, (b) equals()的判断;都相等,因为他们的地址都相等,因此只在常量池中有一份内存空间,地址全部相同;

而String str = new String(“a”);是根据"a"这个String对象再次构造一个String对象;在堆中从新new一块儿内存,把指针赋给栈,

将新构造出来的String对象的引用赋给str。 因此 只要是new String(),则,栈中的地址都是指向最新的new出来的堆中的地址,

(a)“”==“” 是判断地址的,当然不相同;

(b)至于equals,String类型重写了 equals()方法,判断值是否相等,明显相等,因此 equals 是相等的;

接口与回调

回调:某个特定事件发生时应该采取的动作。

为什么要定义接口

1.不能new一个接口,可以声明接口的变量,必须引用实现了接口的类对象
2.使用instanceof可以检查一个对象是否属于某个特定类,也可以使用它来检查一个对象是否实现了某个特定的接口
3.接口可以被扩展,即一个接口可以继承另一个接口
4.在接口中不能包含实例域或静态方法,也不能在接口中实现方法,但却可以包含常量
接口中的方法自动地被设置为public abstract,接口中的域被自动设为public static final。5.虽然在接口中可以让方法省略public,但是在具体实现类必须加上public

java8 新特性

1.可以有静态方法 必须实现
2.如果一个类同时实现了多个接口,可能有同名、同参数的静态方法,使用实现类去调用接口中的静态方法,将不知道具体调用哪一个。所以,java不能实现类.接口静态方法”,只能使用“接口.接口静态方法”去访问。由于在实现类中也可以定义与接口中同名同参的静态方法,但是不能用@Override修饰,所以接口中的静态方法不能被重写
3.可以有default方法 必须实现他 可以重写default方法

辨析

接口的好处就是避免了多继承带来的复杂性和低效性,并可以提供多重继承的好处。接口和抽象类都可以体现多态性,但是抽象类对事物进行抽象,更多的是为了继承,为了扩展,为了实现代码的重用,子类和父类之间体现的是is-a关系;接口则更多的体现一种行为约束,一种规则,一旦实现了这个接口,就要给出这个接口中所有方法的具体实现,也就是说实现类对于接口中所有的方法都是有意义的。
在我们的项目中,为什么数据访问层和业务逻辑层用的是接口而不是抽象类,就是因为当接口设计好之后,下面的人根据规则去实现接口,不能随意扩展,扩展只能交给设计接口的人。只要项目需求不变,接口是不需要改变的,如果经常变动接口,只能说明这个接口是设计的不合理的。
接口的另一大好处就是体现多态性,来实现系统的解耦,如果你的系统比较庞大,、,数据量越来越大,对于性能的要求也越来越高,需要将原先的mysql换成oracle,或者需要将原生的jdbc换成herbinate,那么接口的好处就体现出来了,完全可以用herbinate新写一套实现,而不会惊动上层,上层业务逻辑层是丝毫不会察觉到你有任何变动的。
接口和实现类分离,分工协作,大大提高工作效率。设计接口的人专注于设计接口,这样的系统更能符合客户的需求;一个功能,写逻辑业务的程序员不必等到写数据访问的程序员写完才能开始写,他们可以同时进行,提高了工作效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值