多态的概述
- 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];
}
动态绑定和静态绑定
- 动态绑定
- 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:抽象类和抽象方法必须用abstract关键字修饰
抽象类的成员特点
- A:抽象类的成员特点
- a:成员变量:既可以是变量,也可以是常量。abstract是否可以修饰成员变量?不能修饰成员变量
- b:构造方法:有。
- 用于子类访问父类数据的初始化。
- c:成员方法:既可以是抽象的,也可以是非抽象的。
- C:抽象类的成员方法特性:
- A:面试题1
- 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
- 可以
- 这么做目的只有一个,就是不让其他类创建本类对象,交给子类完成
- B:面试题2
- abstract不能和哪些关键字共存
被abstract修饰的方法没有方法体 static修饰的方法可以用类名.方法调用 无意义
final修饰的子类不能重写 abstract修饰强制子类重写
private 不让子类访问
abstract 让子类访问
- 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:接口用关键字interface表示
抽象类和接口的区别
-
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新写一套实现,而不会惊动上层,上层业务逻辑层是丝毫不会察觉到你有任何变动的。
接口和实现类分离,分工协作,大大提高工作效率。设计接口的人专注于设计接口,这样的系统更能符合客户的需求;一个功能,写逻辑业务的程序员不必等到写数据访问的程序员写完才能开始写,他们可以同时进行,提高了工作效率。