- 继承
继承是指提取不同类的相同属性和方法,使之组成新的类,令拥有此方法和属性的类继承于新形成的类,即父类。(继承关系如下图所示)
由此可知,继承具有单一性,一个子类只能继承一个父类,一个父类可以有多个子类。
继承的关键字有extends和implements。
子类可以重写父类的方法,增加新的属性,但是父类中用private声明的属性和方法不能被子类继承,同时,final修饰的类不能被继承,final类中的属性和方法不一定为final类型。
子类中可以使用super访问父类的方法和属性,使用this定位自身。
继承具有多重性,如图所示:
Java中继承的多重性区别于c++中。
Java中implements可以有多个继承类(实际该implements为实现类,用于实现接口中的抽象方法),其用“,”分隔。如public class C implements A,B{}
继承的优点:实现代码复用,降低代码冗余。
缺点:类与类之间紧密联系,提高了代码的耦合。
关于构造器:子类不继承父类的构造器。
继承归属于java.lang包,故不需要声明使用。Object默认为所有不指明继承关系类的父类,即祖先类。
Java语言建立需要继承的类时,若为同一个类文件中,则需要将被继承的类定义在公共类之外。
继承时,子类的无参构造函数会自动调用父类的无参构造函数。
向上转型与向下转型
向上转型:子类转向父类,即利用子类构造对象,却当作父类来使用。
如:Animal dog=new dog();
由小范围转向大范围,同时一定要注意转型是安全的。
向下转型:将父类转换为对应的子类(类似与强制类型转换)。要求父类原来就是子类转换而来,所以向下转型是一个还原的动作。
如:Dog dogs=(Dog)dog;若类型不同,程序会报错。
向上转型成功后,对象无法调用子类特有的方法和属性,只能向下转型以后才能调用。
例子:USB接口,就是通过判断插入的为什么类型的子类,从而将接口下转型为对应子类。
判断是否为某一类子类可使用instanceof方法,boolean-class
- 重写与重载
重写:子类对父类允许访问的方法,保持方法名和参数类型、数量不变,对核心代码进行重写(外壳不变,核心重写);
重写注意:若父类中抛出异常,则子类中若存在异常,只能为父类中异常的子类或者父类中的异常,不能检测出父类异常的父类。
如:父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,抛出 IOException 异常或者 IOException 的子类异常。
注意事项:
方法重写后,返回类型可以与原来的方法不同,但是必须是父类返回值的派生类。
方法重写其访问权限不能低于被重写的方法的访问权限。
父类的成员方法只能被子类所重写,final修饰的方法不能别重写,static修饰的方法也不能被重写,但是可以被重新声明
构造方法不能被重写。
重载:在同一个类中,方法名相同,参数不同,返回值类型可以不同也可以相同。
重载的方法都必须有一个独一无二的参数列表
被重载的方法必须改变参数列表,返回值类型可以修改,访问权限也可以修改,可以抛出新的异常或者更高权限的异常,同一个类或者子类中可以重载方法,无法以返回值类型判断方法的重载。
方法的重载是一个类多态的体现,方法的重写是父类与子类之间一种多态的体现。
- 多态
解释:同一种行为有多个不同的表现形式或形态的能力。
多态就是同一个接口,使用不同的实例执行不同的操作。
优点:灵活,降低类型耦合,可替换,可扩充,接口性。
虚函数:
在java中,上转型对象在调用经子类重写后的方法时,验证时使用父类的方法验证,但JVM调用时使用子类的该方法。
虚函数的存在就是为了多态。
总结:多态有多种形式,但是其本质通俗而言就是定义未实现的方法,通过需要在后续继承类中或者实现方法中将其根据不同需要实现。最易理解的例子就是USB插口,其本质就是多态的运用。
- 抽象类
定义:对象都是用类进行描述,但是并非所有的类都是用来描述对象的,如果类中没有包含足够的信息来描述具体的实例对象,这样的类就称之为抽象类。
抽象类与普通类的区别在于,其不能实例化对象,除此以外,和普通类没有其他区别。
由抽象类的特性可知,抽象类只能通过继承实现。一个类只能继承一个父类,但是一个类可以继承多个接口。
抽象类不一定含有抽象方法,含有抽象方法的一定是抽象类。
任何子类都必须重写父类中的抽象方法,或者声明自身为抽象类。
抽象类和抽象方法通过abstract定义。
如以下例子:
输出为:
- 封装
定义:对类的方法和属性进行隐藏,即对外部用户隐藏类的某些属性值或者成员方法的实现细节。
通俗理解,通过设定方法和属性的访问权限,增加相应的操作方法,使程序调用该类时,通过其设定的内部方法对属性值进行操作等。
优点:在修改时,可以修改对应类的内部结构和方法,不需要修改调用此方法的程序。
运用恰当可以降低耦合。
- 接口
定义:抽象方法的集合,是一个抽象类型。
接口无法实例化对象,并且接口不包含成员变量,变量需要使用static和final来修饰。即变量类型只能为public static final修饰。
接口使用类来实现,实现接口的类必须实现接口中的所有抽象方法,若未能实现所有方法,则只能定义为抽象类。
Java中可以使用接口来声明一个接口类型的变量,它们可以成为一个空指针,或者绑定在此接口的一个实现类上。
接口中的方法必须都为抽象方法,接口支持多继承,接口中没有构造方法。
接口中的方法不声明访问权限会默认为public abstract,同时接口中的方法修饰也只能如此。变量则只能为public static final。
接口中的方法无法在接口内实现,只能在实现接口的类中实现。
Jdk1.8版本以后的变化:
接口中允许包含静态方法和方法体。
接口中可以包含实现方法,该方法称为默认方法,使用default来修饰。
Jdk1.9版本后变化:
允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。
接口声明格式:
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量//声明抽象方法
}
注意:
接口中的方法和变量都是隐式抽象,不需要完整声明。
在java9之前,接口中的方法只能为public权限,java9之后可以使用private修饰。
接口必须定义在自己的文件之中
调用静态成员方法时,需要使用类名.方法名直接调用
创建接口变量时,可以创建空指针,但是在使用的时候,需要指明其实现类,不能使用空指针调用方法。
接口的继承:
接口的继承与类的继承类似,一个接口可以继承另一个接口,使用关键字extends定义
同时接口可以实现多继承,即一个接口可以继承多个接口。
标记接口:
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
- 枚举
Java中的枚举本质上也为一个类,但是其是特殊类。一般用于表示常量。可在内部和外部中定义枚举类。
其格式为:
enum [类名]{[值],[值],...[值];}
中间使用“,”号分隔。
简单使用为类名.值的方式。
若使用变量接受枚举值,那么需要使用枚举类名进行定义。
如:
枚举值的声明定义都是public static final类型。
枚举的值也可以通过foreach进行迭代。
枚举中有三个方法values(), ordinal() 和 valueOf() 方法,都位于 java.lang.Enum 类中
其主要作用有:
values() 返回枚举类中所有的值。
ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
valueOf()方法返回指定字符串值的枚举常量。
枚举类的成员:
枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。
枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。
需要注意:
枚举类型的构造方法,在初始化每个枚举值的时候才能自动调用,外部不可调用。
如下:
输出如下:
- 包
包的作用:
1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
包的创建:
创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2…].(classname|*);
总结
包的出现,解决了类的命名空间冲突和权限保护问题。
包的声明要放在类的声明之前,类的声明放在源码之前。
包的结构中class和java可以不放在一起,避免分享包结构中造成源码泄露。
包的寻找是从CLASS PATH路径下的目录结合寻找的。
不同包类名相同时,引用需要使用包名定义。
(实际上,在使用某一个包中的类时,系统会自动补全定义)。