子类与父类
- 继承是一种由己有的类创建新类的机制。
- 子类继承一般类的属性和行为,并根据需求增加它自己的新的属性和行为。
- 由继承得到的类称为子类。子类只有一个父类。
- 被继承的类成为父类。一个类可以有多个或零个子类。
子类
- 使用关键字extends来声明子类。
class 子类名 extends 父类名 {
...
}
例如:
class Student extends People {
...
}
在同一包中
子类继承父类中除private外的成员变量和方法,继承的成员变量和方法的访问权限保持不变。
用子类的构造方法创建一个子类的对象时,子类中声明的成员变量、父类的成员变量被分配内存空间,但父类中的private成员变量尽管分配了内存空间,却不作为子类对象的变量。
不在同一包中
继承父类中的private、友好访问权限的成员变量不会被继承。子类只能继承父类中的protected和public访问权限的变量和方法。
用子类的构造方法创建一个子类的对象时,父类的友好成员变量分配了内存空间,但也不作为子类对象的变量。
子类中还有一部分方法是从父类继承的,这部分方法却可以操作这部分未继承的变量。
protected
如果D是C的子类,C是B的子类,B是A的子类,则B、C、D继承A类的protected成员变量和方法。
如果D类在D类中创建一个对象,对象通过“.”运算符访问继承的或自己定义的protected变量和protected方法。
如果Other类中用D类创建一个对象object,则该对象通过“.”运算符访问protected变量和protected方法的权限如下所述。
- 对于子类D自己声明的protected成员变量和方法,只要Other类和D类在同一个包中,object 对象就可以访问这些protected成员变量和方法。
- 对于子类D从父类继承的protected 成员变量或方法,需要追溯到这些protected成员变量或方法所在的“祖先”类,例如可能是A类,只要Other类和A类在同一个包中,olject对象能访问继承的protected变量和protected方法。
instanceof运算符
- 是双目运算符。
- 左面操作元是对象,右面是类。即对象 instanceof 类。
- 当左对象是右类或其子类创建的对象,instanceof运算结果是true,否则是false。
子孙类
- 若C是B的子类,B是A的子类,则C是A的子孙类。
- Object是所有类的祖先类。任何类都是Object类的子孙类。
- 若类的声明中没有使用extends,此类被默认为Object的子类,即声明“class A”与“class A extends Object”是等。
成员变量的隐藏
- 若所声明的成员变量的名字和从父类继承来的成员变量的名字相同(声明的类型可以不同),在这种情况下,子类就会隐藏所继承的成员变量。
- 但子类对象仍然可以调用从父类继承的方法操作被子类隐藏的成员变量。
- 子类继承的方法只能操作子类继承和隐藏的成员变量。子类新定义的方法可以摇作子类继承和子类新声明的成员变量,但无法操作子类隐藏的成员变量。
方法重写
- 如果子类可以继承父类的某个方法,那么子类就有权利重写这个方法。子类通过方法重写隐藏已继承的方法。
- 所谓方法重写,是指子类中定义一个方法,这个方法的类型和父类的方法的类型一致或者是父类的方法的类型的子类型(所谓子类型,是指如果父类的方法的类型是“类”,那么允许子类的重写方法的类型是“子类”),并且这个方法的名字、参数个数、参数的类型和父类的方法完全相同。子类若出现类型不同而方法名相同且参数也相同的两个方法或继承的方法,是不被允许的。
- 不允许降低方法的访问权限,但可以提高访问权限。访问限制修饰符按访问权限从高到低的排列顺序是public、 protected、 友好的、private。
class A { protected float f(float x,float y) { return x-y; } } class B extends A { float f(float x,float y) 非法,友好的,降低了访问权限 { return x+y; } } class C extends A { public float f(float x,float y) 合法,提高了访问权限 { return x*y; } }
- 子类通过方法的重写可以把父类的状态和行为改变为自身的状态和行为。
- 重写方法既可以操作继承的成员变量、调用继承的方法,也可以操作子类新声明的成员变量、调用新定义的其他方法,但无法操作被子类隐藏的成员变量和方法。
- 如果子类想使用被隐藏的方法或成员变量,必须使用关键字super 。
class A
{
Object get()
{
return null; 返回一个空对象
}
}
class B extends A
{
Integer get() Integer是Object的子类
{
System.out.println(new Integer(50)); 输出一个整数类型的值
return new Integer(100); 返回一个Integer对象
}
}
public class C
{
public static void main(String args[])
{
B b=new B();
Integer t=b.get();
System.out.println(t.intValue()); 输出一个整数类型的值
System.out.println(new B()); 输出一个十六进制的引用值
}
}
运行结果:
C:\Users\15939\Desktop>javac C.java
C:\Users\15939\Desktop>java C
50
100
B@15db9742
在这段代码中,Object 和 Integer 是Java中的两个类。
Object 是Java中所有类的基类,它定义了一些通用的方法和属性,例如 toString() 方
法、 hashCode() 方法和 equals() 方法等。所有的对象都可以继承 Object 类,并可
以使用 Object 类中的方法和属性。
Integer 是Java中的一个具体类,代表一个整数。它继承自 Number 类,并实现了一些与
整数相关的方法,例如 intValue() 方法、 toString() 方法等。你可以使用 Integer
类来创建一个代表整数的对象,例如 Integer i = new Integer(10);
intValue() 方法是 Integer 类中的一个方法,用于获取 Integer 对象所表示的整数的
值。该方法返回一个整数类型的值,即 Integer 对象所表示的整数的数值。
super关键字
- 子类一旦隐藏了继承的成员变量或方法,那么子类创建的对象就不再拥有该变量或方法,该变量或方法将归关键字super所拥有。
- 如果在子类中想使用被子类隐藏的成员变量或方法,就需要使用关键字super。
- 当用子类的构造方法创建个子类的对象时,子类的构造方法总是先调用父类的某个构造方法,也就是说,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法。
- 由于子类不继承父类的构造方法,因此,子类在其构造方法中需使用super 来调用父类的构造方法,而且super必须是子类构造方法中的头一条语句,即如果在子类的构造方法中,没有明显地写出super关键字来调用父类的某个构造方法,那么默认地有: super();
- 当在父类中定义多个构造方法时,应当包括- .个不带 参数的构造方法(如上述例子8中的Student类),以防子类省略super 时出现错误
class Student { int number;String name; Student() 无参无语句构造方法 { } Student(int number,String name) { this.number=number; this.name=name; System.out.println("我的名字是:"+name+ "\n学号是:"+number); } } class UniverStudent extends Student { boolean 婚否; UniverStudent(int number,String name,boolean b) { super(number,name); 调用父类的构造方法:Student(int number,String name) 婚否=b; System.out.println("婚否="+婚否); } } public class C { public static void main(String args[]) { UniverStudent zhang=new UniverStudent(9901,"何晓林",false); } }
final关键字
final关键字 可修饰类、成员变量、方法中的局部变量。
使用final把类声明成final类。final类不能被继承,无子类。
格式:
final class A
{
}
如果用final修饰父类中的一一个方法,那么这个方法不允许子类重写。即不允计子类隐藏可以继承的final方法。
如果成员变量或局部变量被修饰为final, 那它就是常量。由于常量在运行期间不允许再发生变化,所以常量在声明时没有默认值,这就要求程序在声明常量时必须指定该常量的值。
class A
{
final double PI=3.1415926;// PI是常量
public double getArea(final double r)
{
return PI*r*r;
}
public final void speak()
{
System.out.println("您好,How's everything here ?");
}
}
public class Example5_9
{
public static void main(String args[])
{
A a=new A();
System.out.println("面积:"+a.getArea(100));
a.speak();
}
}
对象的上下转型
上转型
假设Animal类是Tiger类的父类,当用子类创建一个对象b, 并把这个对象b的引用放到父类的对象a中,则称对象a是对象b的上转型对象。即子类对象给父类引用变量赋值。
假如对象之间存在继承关系,那么对象之间可以进行转换。如果两种类型之间没有继承关系,那么将不允许进行类型转换。
格式:
Animal a; <=> Animal a;
a=new Tiger(); Tiger b=new Tiger();
a=b;
上转型对象不能操作子类新增的成员变量和方法。
上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法。上转型对象操作子类继承的方法或子类重写的实例方法,其作用等价于子类对象去调用这些方法。因此,如果子类重写了父类的某个实例方法后,当对象的上转型对象调用这个实例方法时一定是调用了子类重写的实例方法。
如果子类重写了父类的静态方法,那么子类对象的上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法。
不要将父类创建的对象和子类对象的上转型对象混淆。
父类对象与向上转型的父类变量之间的区别是什么?
向上转型的父类变量和父类对象之间的区别是在调用父类中被重写的方法时,父类对象调用的是父类的方法,而子类向上转型得到的父类变量是调用子类中重写的方法。
怎么区分新增变量、方法?
要区分子类中新增的变量和继承的变量,可以使用以下方法:
1. 使用类型检查:在转型后的父类对象上,可以使用类型检查来确定该对象是否
是子类的实例。例如,可以使用instanceof 运算符来检查对象是否是某个特定子类的实例。
2. 使用类型转换:在转型后的父类对象上,可以使用类型转换将其转换回子类。
这样,就可以访问子类中新增的变量和方法。但是,这种方法需要注意可能会丢失
父类中定义的变量和方法。
要区分子类中新增的方法和继承的方法,可以使用以下方法:
1. 使用方法签名:子类中新增的方法通常会有不同的方法签名,因此可以通过方法
签名来区分它们。
2. 使用反射:使用 Java 反射 API,可以动态地获取类的方法列表,并检查每个方法的
签名和可见性。这样,就可以确定子类中新增的方法。
向上转型之后的父类变量可以访问所有的父类属性和方法,包括private修饰的变量和方法,但是想要直接访问private修饰的变量和方法的前提是在父类中,因为private访问修饰符控制的范围就是一个类中可以访问。不在一个类中,不能直接访问。
下转型
对象向下转型:向上转型的父类对象给子类引用变量赋值。
格式: Fahter father = new Son(); 向上转型
Son son = (Son) father; 向下转型
使用向下转型,必须先向上转型。
向上转型是可以省略转换的类型,但是向下转型必须显示写出类型。
只有通过向上转型的父类引用变量可以向下转型;如果父类引用的对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException异常——类型转换异常。这个异常可以通过instanceof关键字避免,如果左边的对象可以给右边的类赋值,返回true,否则返回false。
class 类人猿
{
void crySpeak(String s)
{
System.out.println(s);
}
}
class People extends 类人猿
{
void computer(int a,int b) 新增的方法
{
int c=a*b;
System.out.println(c);
}
void crySpeak(String s) 重写的方法
{
System.out.println("***"+s+"***");
}
}
public class Example5_10
{
public static void main(String args[])
{
类人猿 monkey;
People geng = new People();
System.out.println(geng instanceof People); 输出true
monkey = geng ; monkey是People对象geng的上转型对象
System.out.println(monkey instanceof People); 输出true
System.out.println(monkey instanceof 类人猿); 输出true
monkey.crySpeak("I love this game");
等同于geng.crySpeak("I love this game");
People people=(People)monkey; 把上转型对象强制转化为子类的对象
people.computer(10,10);
monkey.computer(10,10); 是错误的,因为computer方法是子类新增的方法。
}
}
运行结果:
C:\Users\15939\Desktop>javac C.java
C:\Users\15939\Desktop>java C
true
true
true
***I love this game***
100