继承
继承在一定程度上能实现代码重用,并且通过继承实现已有类的新特性扩充,让程序更具有可拓展性。
继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。
Java不支持多重继承,只能直接继承一个父类(单继承),但也能使用多层继承。
子类对象必须能够替换掉所有父类对象。 Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型 。
Animal animal = new Cat();
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问, 要通过父类提供公共的方法去访问。
子类必须调用父类的构造器, 完成父类的实例化 。当创建子类对象时,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作。
super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
java 所有类都是Object 类的子类, Object 是所有类的基类。
父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)。
方法重写
重写(Overriding)
重写一般用于重写(Overriding)父类的方法,但不改变参数列表和返回类型,保持相同的方法签名。 子类方法的访问权限必须大于等于父类方法,子类方法不能缩小父类的访问权限。 final关键字定义不能被重写的类或方法,或者定义不能被修改的常量(引用类型的被引用对象本身是可修改的)。定义全局常量名称必需全部大写。
final int x = 1; // x = 2; // cannot assign value to final variable 'x' final A y = new A(); y.a = 1;
private方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。
重载(Overloading) 与它类似的操作是重载(Overloading)。重载方法名称相同,但是参数列表不会相同(同名不同参)。
super与this
区别 | super | this |
---|---|---|
访问属性,方法 | 子类查找,依次向父类查找 | 从父类查找 |
调用构造器 | 调用本类构造 | 调用父类构造 |
其他 | 当前对象 | 子类访问父类对象 |
注解
Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 Java内置注解, 包括
@Override
、@Deprecated
和@SuppressWarnings
,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。
- 准确覆写@Override
表示当前的方法定义将覆盖父类中的方法 - 过期声明@Deprecated
表示代码被弃用,如果使用了被@Deprecated注解的代码则编译器将发出警告 - 压制警告@SuppressWarnings
表示关闭编译器警告信息
class A{
public void test() {
}
}
class B extends A{
/**
* 重载父类的test方法
*/
@Override
public void test() {
}
/**
* 被弃用的方法
*/
@Deprecated
public void oldMethod() {
}
/**
* 忽略告警
*
* @return
*/
@SuppressWarnings("rawtypes")
public List processList() {
List list = new ArrayList();
return list;
}
}
多态
方法多态性和对象多态性
方法多态性 方法重载、方法覆写
对象多态性
- 对象向上转型 统一接收或返回的参数类型
class Instrument {
public void play() {
System.out.println("Instrument is playing...");
}
}
class Wind extends Instrument {
public void play() {
System.out.println("Wind is playing...");
}
}
class Percussion extends Instrument {
public void play() {
System.out.println("Percussion is playing...");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
List<Instrument> instruments = new ArrayList<>();
//向上转型
instruments.add(new Wind());
instruments.add(new Percussion());
for (Instrument instrument : instruments){
instrument.play();
}
}
}
- 向下转型
产生向下转型前一定会发生向上转型,用于获取子类特殊的方法
instrument.play();
// 向下转型
Percussion percussion = (Percussion) instrument
instanceof关键字
判断它左边的对象是否是它右边的类的实例.
上面的向下转型前可以通过instanceof判断是否进行过向上转型
if(instrument instanceof Percussion)
Object类
Java中唯一一个不存在继承关系的类
可以用来接收任意类的对象,包括数组那样的引用数据类型也可以 直接输出对象时会自动调用object类的toString方法,可以通过覆写该方法指定输出对象的信息
public class ObjectDemo {
public static void main(String[] args) {
Object obj = new int [] {1,2,3};// 向上转型
if(obj instanceof int[]){
int res[] = (int []) obj;// 向下转型
System.out.println("向下转型"+ res);
System.out.println("向下转型"+ res.toString());
}
}
}
quals()方法 Object类的equals方法
进行覆写
class EqualExample {
private int x;
private int y;
private int z;
public EqualExample(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || !(obj instanceof EqualExample)) return false;
EqualExample e = (EqualExample) obj;
if(x != e.x) return false;
if(y != e.y) return false;
return z == e.z;
}
}
抽象类与接口
通过使用abstract关键字来定义抽象类和方法,抽象类和方法并不完善,只是约定结构。
子类必须强制性覆写父类抽象方法,除非它自己也声明为abstract类。
抽象类不能被实例化,抽象方法不能有主体,即不能实现
抽象类不一定要包含abstract方法。 也就是说,抽象类可以没有abstract方法,但是一旦类包含了abstract方法,则这个类必须声明为abstract
abstract只能修饰类和方法,不能修饰属性和其它的,抽象类可以有任意成员,也可以拥有自己的构造方法[抽象类本质还是类]。
抽象方法不能使用private、final 和static来修饰,因为这些关键字都是和重写相违背的。
public class AbstractClassDemo {
public static void main(String[] args) {
// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
// static关键字 不受实例化对象或结构的限制
AbstractClassExample ac3 = AbstractClassExample.getInstance();
ac2.fun1();
ac3.fun1();
}
}
abstract class AbstractClassExample{
protected int x;
private int y;
public abstract void fun1();
public void fun2() {
System.out.println("fun2");
}
public static AbstractClassExample getInstance(){
return new AbstractExtendClassExample();
}
}
class AbstractExtendClassExample extends AbstractClassExample{
@Override
public void fun1() {
System.out.println("func1");
}
}
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造但子类总体上会保留抽象类的行为方式。
包装类
拆箱:从包装类中获取基本数据类型
装箱:将基本数据类型放入包装类中
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
包装类对象都是引用类型,不会相等。1.5版本前只能通过手动方式实现装箱。
自动装箱都是通过包装类的 valueOf()方法来实现的.自动拆箱都是通过包装类对象的 xxxValue()来实现的
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);// false
Integer x1 = Integer.valueOf(123);
Integer y1 = Integer.valueOf(123);
Integer x2 = Integer.valueOf(128);
Integer y2 = Integer.valueOf(128);
System.out.println(x1 == y1);// true
System.out.println(x2 == y2);// false
在 Java 8 中,Integer 缓存池的大小默认为 -128~127。所以会导致上面情况的产生
Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取同一个对象的引用
// 先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
包装对象的数值比较,不能简单的使用==,虽然-128 到 127 之间的数字可以使用缓存池,但是这个范围之外还是需要使用 equals 比较。
由于自动拆箱,如果包装类对象为 null,那么自动拆箱时就有可能抛出空指针异常NPE。
如果一个for循环中有大量拆装箱操作,也会浪费很多资源。