一、继承性
Java中通过继承实现代码复用,所有的类都是通过直接或间接地继承java.lang.Object类得到的。Java语言中任何一个对象都是Object类的实例。
1、创建子类
Java中,使用关键字ectends实现继承,格式为:
[类修饰]class 类名 extends 父类名 [implements 接口列表]{//类声明
}
继承而得到的类称为子类,被继承的类称为父类,基类或超类 。Java中不支持多重继承,即一个类从多个父类派生的能力。子类可以继承父类中所有可被
子类访问的成员变量和成员方法。
例如:
class People{
String id, name;
int age;
People(){
id = "00000";
name = null;
age = 0;
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
}
public class Student extends People{
String number;//定义自己的成员变量:学号
public static void main(String[] args) {
Student s = new Student();
s.age = 20;
s.number = "15";
s.setName("黎明");//继承父类的成员方法
System.out.println("学号:"+s.number);
System.out.println("姓名:"+s.getName());
System.out.println("年龄:"+s.age);
}
}
2、对象的上转型对象
有了继承,就出现上转型对象。上转型对象,即将子类实例化的对象的引用赋值给父类声明的变量。即定义了子类对象,但把这个对象的引用放到父类的对象中。
例如, 假设A类是B类的父类
A a;
A = new B();
或
A a;
B b = new B();
a = b;
称这个对象a是对象b的上转型对象。
上转型对象的特点:
- 上转型对象不能操作子类新增的成员变量,不能使用子类新增的方法;
- 上转型对象可以操作子类继承或隐藏的成员变量,也可以使用子类及小孩能的或重写的方法;
- 如果子类重写了父类的某个方法,当上转型对象调用这个方法时一定是调用了这个重写的方法;
例如:
class People{
protected String name = "张三";
protected int age, number;
String getName() {
return name;
}
protected String getInfo(String s, int a, int n) {
name = s;
age = a;
return "父类姓名:"+name+"\n"+"父类年龄:"+age+"\n"+"父类工号:"+number+"\n";
}
}
public class Student extends People {
protected String name = "李四";
protected String getInfo(String s, int a) {
name = s;
age = a;
return "子类姓名:"+name+"\n"+"子类年龄:"+age;
}
protected String getInfo(String s, int a, int n) {//方法重写
name = s;
age = a;
number = n;
return "子类姓名:"+name+"\n"+"子类年龄:"+age+"\n"+"子类工号:"+number;
}
public static void main(String[] args) {
People p = new Student();//p为向上转型对象
//System.out.println(p.getInfo(p.getName(), 20));//非法,向上转型对象不可以访问子类新增成员
System.out.println(p.getInfo(p.getName(), 40, 10000));//访问子类重写的方法, 隐藏的变量
Student s = (Student)p;
System.out.println(s.getInfo(s.getName(), 20));
}
}
3、隐藏于覆盖
子类可以继承父类的变量和方法,也可以在子类中定义自己的变量和方法,以实现继承后的变异。
隐藏就是子类影藏了父类的变量或方法,那么,子类不能访问父类杯隐藏的变量与方法,但是,如果用子类创建了一个对象,并把这个对象的引用放到父类的对象中时,
即为上转型对象,子类就可以访问父类杯隐藏的变量或方法。
覆盖就是子类覆盖了父类的变量或方法,那么,子类不能访问父类被覆盖的变量或方法,将子类上转型成父类对象或同样不能访问被覆盖的变量或方法。
class ParentClass{
private static String privateField = "父类变量-private";
String filendlyFileld = "父类变量-friendly";
protected static String protectedField = "父类变量-protected static";
public String publicField = "父类变量-public";
public String getPrivateFiledValue() {
return privateField;
}
}
public class SubClass extends ParentClass {
private static String privateField = "子类变量-private";
String friendlyField = "子类变量-friendly";
protected static String protectedField = "子类变量-protected static";
public String publicField = "子类变量-public";
public String getPrivateFieldValue() {
return privateField;
}
public static void main(String[] args) {
ParentClass parentClass = new ParentClass();
System.out.println("ParentClass parentClass = new ParentClass()");
System.out.println(parentClass.getPrivateFiledValue());
System.out.println(parentClass.filendlyFileld);
System.out.println(parentClass.protectedField);
System.out.println(parentClass.publicField);
ParentClass subClass = new SubClass();
System.out.println("ParentClass subClass = new SubClass();");
System.out.println(subClass.getPrivateFiledValue());
System.out.println(subClass.filendlyFileld);
System.out.println(subClass.protectedField);
System.out.println(subClass.publicField);
SubClass subCl = new SubClass();
System.out.println("SubClass subCl = new SubClass();");
System.out.println(subCl.getPrivateFiledValue());
System.out.println(subCl.filendlyFileld);
System.out.println(subCl.protectedField);
System.out.println(subCl.publicField);
}
}
从上列可以看出,变量访问控制符为缺省、protected和public时,当变量类型是父类时,不管创建的对象时父类还是子类,都不存在变量覆盖的问题,变量杯隐藏;变量
访问控制符为private时,不管是子类对象还是子类对象,同名对象均覆盖。
在继承的情况下,如果父类和子类存在同名的方法会出现什么情况?这就是方法的隐藏于覆盖问题。
Java中方法在继承时的覆盖和隐藏规则:
- 父类的静态方法可被子类的同名静态方法隐藏。
- 父类的实例方法可以被子类的同名实例方法覆盖。
- 不能用子类的静态方法隐藏父类的实例方法。
- 不能用子类的实例方法覆盖父类中的静态方法。
- 最终方法不能被覆盖。
- 子类方法的访问权限不能低于父类方法的访问权限。
4、this和super
(1)this
this指明当前对象,当一个方法引用它自己类的实例变量和方法时,每个引用前都隐含着“this”。当局部变量或更高参数与成员变量同名时,如果在成员方法中使用成员变量
,前面必须带“this”。
另外,this还可以用在重载的构造方法中,用来引用其他的构造方法,一般格式为this[参数列表]。
注意:this不能在静态方法中做显示引用。
class People1 {
protected String name, id;
protected int age;
People1(String name){//第一构造方法
this.name = name;
}
People1(String name, int age){//第二构造方法
this(name);//调用第一构造方法
this.age = age;
}
People1(String name, int age, String id){//第三构造方法
this(name, age);//调用第二构造方法
this.id = id;
}
protected String getInfo(String name, int age, String id) {
this.name = name;
this.age = age;
this.id = id;
return "姓名:"+this.name+"\n"+"年龄:"+this.age+"\n"+"工号:"+this.id+"\n";
}
public static void main(String[] args) {
People1 p = new People1("Mike", 30, "00153");
String str = "姓名:"+p.name+"\n"+"年龄:"+p.age+"\n"+"工号:"+p.id+"\n";
System.out.println(str);
System.out.println(p.getInfo("Jehn", 20, "00155"));
}
}
(2
)、super
super关键字的作用是用于子类引用父类的成员,如变量、方法或构造方法。
当super用于父类中的属性或方法时,使用的格式是“super.成员”,此时,父类属性或方法必须是那些protected或public等可以让子类访问的属性或方法,使用super可以在子类中访问父类中的变量或重写方法。
此外,使用super[参数列表]就可以调用父类的构造方法了,如果父类中有多个构造方法,系统将自动根据super中的参数个数和参数类型来匹配父类中的构造方法。
class People1{
protected String name, id;
protected int age;
People1(String name, int age, String id){
this.name = name;
this.age = age;
this.id = id;
}
protected String getInfo(String name, int age, String id) {
this.name =name;
this.age = age;
this.id = id;
return "父类姓名:"+name+"父类年龄:"+age+"\n"+"父类工号:"+id+"\n";
}
}
public class Student1 extends People1{
Student1(String name, int age, String id){
super(name, age, id);
}
public String getInfo(String name, int age, String id) {
this.name = name;
this.age = age;
this.id = id;
return "子类姓名:"+name+"\n"+"子类年龄:"+age+"\n"+"子类工号:"+id+"\n";
}
String getSuperInfo() {
return super.getInfo(name, age, id);
}
public static void main(String[] args) {
People1 p = new People1("Mike", 30, "0001");
Student1 s = new Student1("John", 20, "152");
System.out.println(p.getInfo(p.name, p.age, p.id));
System.out.println(s.getInfo(s.name, s.age, s.id));
System.out.println(s.getSuperInfo());
}
}
5、构造方法的继承
父类的构造方法也可以被子类继承,使用时应当遵循以下原则:
- 子类可以在自己的构造方法中使用super(参数列表)来调用父类的构造方法。使用this(参数列表)来调用本类的构造方法。
- 如果调用父类的构造方法,则必须卸载子类构造方法的第一行。
- 如果子类的构造方法没有显示地调用父类的构造方法,则系统默认调用父类的无参构造方法。
- 如果子类构造方法中即没有显示地调用父类的构造方法,而父类中又没有无参的构造方法,则编译出错。
class Animal{
Animal(){
System.out.println("动物不会说话");
}
}
class Person extends Animal{
String str;
Person(String str){
//隐式调用父类的无参构造方法 Animal()
this.str = str;
System.out.println("人类使用"+str+"交流。");
}
Person(){
//隐式调用父类的无参构造方法 Animal()
this("语言");
}
}
public class Chinese extends Person{
String s;
Chinese(String s){
super("肢体动作");//显示调用父类的构造方法Person(String str)
this.s = s;
System.out.println("中国人的语言是:"+s+".");
}
Chinese(){
//隐式调用父类的无参构造方法
System.out.println("中国人最棒!");
}
public static void main(String[] args) {
Chinese t1 = new Chinese("汉语");
Chinese t2 = new Chinese();
}
}
二、多态性
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序
运行期间才确定,即一个引用变
量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个
类中实现的方法,
必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,
这样,不用修改源程序代码,
就可以让引用变量绑定
到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以
改变程序运行
时所绑定的具体
代码,让程序可以选择多个运行状态,这就是多态性。
多态的实现基础是继承!
多态有两种形式:
(1)引用的多态:父类的引用可以指向本类的对象,还可以指向子类的对象。
Animal obj = new Animal();
Animal obj1 = new Dog();
(2)方法的多态:创建本类对象时,调用的方法为本类的方法,创建子类对象时,调用的方法为子类的重写的方法或继承的方法。
public class Wine {
public void fun1(){
System.out.println("Wine 的Fun.....");
fun2();
}
public void fun2(){
System.out.println("Wine 的Fun2...");
}
}
public class JNC extends Wine{
/**
* @desc 子类重载父类方法
* 父类中不存在该方法,向上转型后,父类是不能引用该方法的
* @param a
* @return void
*/
public void fun1(String a){
System.out.println("JNC 的 Fun1...");
fun2();
}
/**
* 子类重写父类方法
* 指向子类的父类引用调用fun2时,必定是调用该方法
*/
public void fun2(){
System.out.println("JNC 的Fun2...");
}
}
public class Test {
public static void main(String[] args) {
Wine a = new JNC();
a.fun1();
}
}
-------------------------------------------------
Output:
Wine 的Fun.....
JNC 的Fun2...
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载
该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法.