相同点:
都是不断向上抽取而来的。
不同点:
①抽象类需要被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
②抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
接口中只能定义抽象方法,必须由子类去实现。
③抽象类的继承,是is a关系,定义该体系的基本共性内容。
接口的实现是like a关系。
7, 多态,一个对象变量可以指示多种实际类型的的现象称为多态。是"IS -A“规则。
体现:
父类或者接口的引用指向或者接收自己的子类对象。
作用:
多态的存在提高了程序的扩展性和后期可维护性。
前提:
①需要存在继承或者实现关系。
②需要有覆盖操作。
好处:
提高了代码的扩展性,前期定义的代码可以使用后期的内容。
弊端:
前期定义的内容不能使用(调用)后期子类的特有内容。
JAVA中,子类数组引用可以转化为超类数组引用,不需要强制类型转换,但是需要注意,时时刻刻记住创建数组的类型。
instanceof :用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断,NULL不会报错。
成员变量
编译时:参考引用型变量所属的类中的是否有调用的成员变量。有,编译通过,没有,编译失败。
运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
简单说:编译和运行都参考等号的左边。
成员函数(非静态)
编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
静态函数
编译时:参考的是对象所属的类中是否有调用的函数。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译和运行看左边。
8, 动态绑定,对象方法调用时步骤,候选方法->重载解析->调用。
如果是private方法,static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,这种调用方式叫做静态绑定。与此对应的是,调用的方法依赖于隐式的参数类型,并且在运行时实现动态绑定。编译器采用动态绑定的方式生成一条调用其方法的指令。当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与X所引用对象的实际类型最合适的那个方法。
每次调用方法都要进行搜索,时间开销相当大,因此,虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用的方法。
9,Object类,所有类的超类, 用==比较基本类型, equals比较对象域,比较两个对象是否具有相同的引用。
equals()方法:自反性(x!=null, x.equals(x)=ture),对称性( x.equals(y)= y.equals(x) ),传递性(x=y, y= z, 则 x=z),一致性(如果引用对象没有发生变化,则结果也不会变),如果子类能拥有自己的相等概念。则对称性需求将强制采用getClass进行检测。如果超类决定相等的概念,那么就可以用instanceof进行检测,这样可以用于不同子类进行相等检测。@override防止没有覆盖父类方法。
hashcode()方法,有对象导出的一个整形值。默认是对象的存储存地址。String中是有内容组成的。如果重新定义equals方法,则也需要重新定义hashCode方法。他们的定义必须一致。
clone()方法,深拷贝则需要用clone()方法,他是protect方法,用户不能直接调用它。一般情况下:如果要深拷贝,必须要重新定义clone方法。
对于每一个类,需要作以下判断:
1, 默认的clone方法是否满足需求。
2,默认的clone方法是否能够通过调用可变子对象的clone得到修补。
3, 是否不应该使用clone。
要选择1或者2,则:
实现Cloneable接口。(该接口仅起一个标记作用,如果一个对象需要克隆,而没有实现cloneable接口,则会抛出一个异常。现在可以返回object类的子类,斜边返回类型)
使用public访问修饰符重新定义clone方法。
10, 对象包装器,Integer ,Long, Double, Float, Short, Byte,Chararter,Void, Boolean(前6个派生于Number类)。装箱:XXX.valueOf() 拆箱:XXX.xxvalue(),自动装箱规范要求boolean,byte,char<= 127. 介于-128~127之间的short 和int被包装到固定的对象中。
11, 可变参数,当成数组使用。
枚举类:可以直接用==比较,不需要equals比较。如果需要,可以在枚举类中添加一些构造器、方法和域。当然,构造器只是在构造枚举常量是被调用。所有的枚举类型都是Enum类的子类,常用方法toSTring()。valueOf(class, string), values().
12, 接口与回调,回调是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件。由于对象可以携带一些附加的信息,所以传递一个对象比传递一个函数要灵活得多。
13, 内部类,将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。
为什么要使用内部类?
内部类可以直接访问外部类中的成员,包括私有成员。
内部类可以对同一个包中的其他类隐藏。
当要定义一个回调函数不想写大量的代码,使用匿名内部类比较便捷。
对外部类的引用:outer.this
在外部类作用域之外,可以这样使用内部类:outerclass.innerclass
编译器会把内部类翻译成用$分割外部类名和内部类名的常规类文件。
编译器会生成this$0的变量外部对象引用。所有的名字都会加上outer$。
内部类访问外部类私有域,会在外部类生成一个静态方法。。这样有安全隐患。同时还会生成构造器。
内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。
如果内部类中定义了静态成员,该内部类也必须是静态的!
内部类定义在局部位置上,也可以直接访问外部类中的成员。局部类修饰符不能用public private 访问说明符号进行申明,可以对外部世界完全隐藏起来,即使外部类方法也不能访问。
匿名内部类:
定义:
就是内部类的简化写法。
前提:
内部类可以继承或实现一个外部类或者接口。
格式:
new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
简单理解:
就是建立一个带内容的外部类或者接口的子类匿名对象。
什么时候使用匿名内部类呢?
通常在使用方法是接口类型参数,并且该接口中的方法不超过三个时,可以将匿名内部类作为参数传递。
由于构造器的名字必须和类名一致,而匿名类没有类名,所以,匿名类不能有构造器,取而代之将构造器传递给超类构造器。
声明在接口中的内部类默认自动为public和Static.
技巧:双括号初始化。
ArrayList<String> friends = new ArrayList();
friends.add("nihao");
friends.add("hell:");
invite(friends);
可简化为:invite(new ArrayList<String>(){{ add("Hary"); add("Tony"); }}); 构造代码块。