进入第二篇,接触核心技术。
继承在面向对象开发思想中是一个非常重要的概念,它使整个程序框架具有一定的弹性。在程序中复用一些已经定义完善的类不仅可以减少软件的开发周期,也可以提高软件的可维护性和可拓展性。
继承的基本思想是基于某个父类进行拓展,得到一个新的子类。子类可以继承父类原有的属性和方法,也可以增加原来父类所不具有的属性和方法,或者重写父类中的一些方法。
语法
Child extends Parents
示例
public class Child extends Parents{
}
举例
父类电脑Computer,子类平板电脑Pad
父类:
public class Computer {
String screen = "液晶显示屏";
void StartUp(){
System.out.println("电脑正在开机。");
}
}
子类:
public class Pad extends Computer {
}
另一个类:
public class Demo {
public static void main(String[] args) {
Computer com = new Computer();
Pad iPad = new Pad();
System.out.println(com.screen);
com.StartUp();
System.out.println(iPad.screen);
iPad.StartUp();
}
}
子类继承了父类的成员变量和成员方法。
液晶显示屏
电脑正在开机。
液晶显示屏
电脑正在开机。
子类可以有独有的变量和方法:
子类改为:
public class Pad extends Computer {
String battery = "5000mAh";
void TurnOnNet(){
System.out.println("打开5G网络。");
}
}
运行:
public class Demo {
public static void main(String[] args) {
Computer com = new Computer();
Pad iPad = new Pad();
//继承父类
System.out.println(com.screen);
com.StartUp();
System.out.println(iPad.screen);
iPad.StartUp();
//子类独有
iPad.TurnOnNet();
System.out.println(iPad.battery);
}
}
液晶显示屏
电脑正在开机。
液晶显示屏
电脑正在开机。
打开5G网络。
5000mAh
父类中新建方法:
void openPicture(){
System.out.println("鼠标打开 图片.jpg");
}
子类中进行重写:
public class Pad extends Computer {
String battery = "5000mAh";
void TurnOnNet(){
System.out.println("打开5G网络。");
}
void openPicture(){
System.out.println("触屏打开 图片.jpg");
}
}
输出:
com.openPicture();
iPad.openPicture();
鼠标打开 图片.jpg
触屏打开 图片.jpg
子类没有权限调用父类中被修饰为private
的方法,只可以调用父类中修饰为public
或者protected
的成员方法。
super.property;
super.method();
super关键字代表父类的对象。
super.property
父类的属性
super.method()
父类的方法
public class Pad extends Computer {
public void action(){
super.action();
}
}
父类中加入方法:
String sayHello(){
return "欢迎使用!";
}
子类中重写:
String sayHello(){
return super.sayHello()+"iPad!";
}
输出:
//super关键字
System.out.println(com.sayHello());
System.out.println(iPad.sayHello());
欢迎使用!
欢迎使用!iPad!
将父类中的“欢迎使用!”改为“Welcome!”后的输出:
Welcome!
Welcome!iPad!
public Pad(){
this.screen = super.screen;
}
public Pad(){
super();
}
Java语言中,一个类只可以有一个父类
如果需要同时继承两个类,需要使用多重继承
简单来说,就是套娃。
class parent1{
}
class parent2 extends parent1{
}
class child extends parent2{
}
子类不仅会覆盖父类的方法,还会覆盖父类的属性
创建类:
class parent2{
String name;
public parent2(String name){
this.name = name;
}
}
class Child extends parent2{
String name = "Tom";
public Child(String name) {
super(name);
}
}
main方法中:
Child child = new Child("Jack");
System.out.println(child.name);
输出:
Tom
可见子类覆盖了父类的属性。
虽然name
与父类的属性同名,仍然属于子类独有的属性。
Java中所有的类都直接或者间接继承了java.lang.Object
类。
Object类是比较特殊的类,它是所有类的父类,是Java类层中的最高层的类。
在创建一个类时,例如:
public class Person{
}
实际上完整的是:
public class Person extends Object{
}
只是由于所有的类都是Object
类的子类,所以后面的extends Object
可以省略。
返回对象执行时的Class实例。
创建一个Object数组,因为Object类是所有类的父类,所以Object数组可以存放任何一个类:
先创建一个Object数组,然后存放不同类型的元素,遍历数组:
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Demo();
for (Object obj:arr){
System.out.println(obj.getClass());
}
}
}
输出:
class java.lang.Object
class java.lang.String
class java.lang.Integer
class Demo
输出的是类的全名,class表示这是一个类,后面是类名
将对象返回为字符串形式,如果子类不重写方法,将返回类名+@+十六进制的哈希值
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Demo();
for (Object obj:arr){
System.out.println(obj.toString());
}
}
}
输出:
java.lang.Object@10f87f48
我是字符串。
123456
Demo@b4c966a
第一四行按格式输出,第二三行返回了String
和Integer
的字面值,因为String
、Integer
类重写了该方法。
重写toString
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Demo();
for (Object obj:arr){
System.out.println(obj.toString());
}
}
@Override
public String toString() {
return "Demo类";
}
}
输出
java.lang.Object@10f87f48
我是字符串。
123456
Demo类
这里的System.out.println(obj.toString());
中的.toString()
是可以删掉的。
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Demo();
for (Object obj:arr){
System.out.println(obj);
}
}
@Override
public String toString() {
return "Demo类";
}
}
输出结果相同:
java.lang.Object@10f87f48
我是字符串。
123456
Demo类
比较两个对象是否相等
默认比较地址是否相等
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Demo();
System.out.println(arr[0].equals(arr[3]));
}
}
false
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = arr[0];
System.out.println(arr[0].equals(arr[3]));
}
}
true
public class Demo {
public static void main(String[] args) {
Object[] arr = new Object[4];
arr[0] = new Object();//实例化了一个Object对象给arr[0]
arr[1] = new String("我是字符串。");
arr[2] = new Integer("123456");
arr[3] = new Object();
System.out.println(arr[0].equals(arr[3]));
}
}
false
重写:
import java.util.Objects;
public class Person {
String name;
String ID;
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
p1.name = "XiaoMing";
p1.ID = "123";
p2.name = "XiaoHong";
p2.ID = "456";
p3.name = "XiaoMing";
p3.ID = "123";
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p2.equals(p3));
}
@Override
public boolean equals(Object o) {
Person p = (Person)o;
boolean b1 = this.name.equals(p.name);//字符串的equals方法,判断字面值是否一样
boolean b2 = this.ID.equals(p.ID);
return b1&&b2;
}
@Override
public int hashCode() {
return Objects.hash(name, ID);
}
}
输出:
false
true
false
子类的对象转为父类的对象
用父类声明对象,用子类实例化对象
Parents Object = new Child();
Person tom = new Student();
一个人叫tom,是一个学生。
创建父类Person
,在其中创建一个构造方法:
public class Person {
public Person(String name){
System.out.println(name);
}
}
创建子类Students
:
public class Students extends Person {
public Students(String name) {
super(name);
}
}
主方法:
public class Up {
public static void main(String[] args) {
Person TOM = new Person("TOM");
}
}
输出:
TOM
实例化时把Person
改成Students
:
public class Up {
public static void main(String[] args) {
Person TOM = new Students("TOM");
}
}
输出相同,还是:
TOM
将父类的对象强制转换为子类的对象
必须使用强制转换
Parents p = new Parents();
Child c = (Child)p;
Person tom = new Person();
Doctor dr_tom = (Doctor)tom;
public class Down {
public static void main(String[] args) {
Person tom = new Students("tom");
Person jack = new Person("jack");
Doctor dr = (Doctor)tom;
}
}
报错,Error:(5, 29) java: 不兼容的类型: Person无法转换为Doctor
public class Down {
public static void main(String[] args) {
Person tom = new Students("tom");
Person jack = new Person("jack");
Doctor dr = (Doctor)jack;
}
}
报错,Error:(5, 29) java: 不兼容的类型: Person无法转换为Doctor
public class Down {
public static void main(String[] args) {
Person tom = new Students("tom");
Person jack = new Doctor("jack");
Doctor dr = (Doctor)jack;
}
}
正常输出:
tom
jack
boolean result = child instanceof parents;
判断对象是否继承自类
如果前是后的子类,返回true
,如果不是,返回false
public class Computer {//电脑
public static void main(String[] args) {
Pad iPad = new Pad();
HuaweiPad MatePad = new HuaweiPad();
System.out.println("Pad是否继承自Computer:"+(iPad instanceof Computer));
System.out.println("HuaweiPad是否继承自Pad:"+(MatePad instanceof Pad));
System.out.println("HuaweiPad是否继承自Computer:"+(MatePad instanceof Computer));
System.out.println("HuaweiPad是否继承自Object:"+(MatePad instanceof Object));
}
}
class Pad extends Computer{//平板电脑
}
class HuaweiPad extends Pad{//华为平板电脑
}
父类的父类返回的也是true
,甚至可以是Object
两个没有任何继承关系的类不能用instanceof
public class Demo {
public static void main(String[] args) {
System.out.println("调用add(int a):"+add(1));
System.out.println("调用add(int a,int b):"+add(1,2));
}
static int add(int a){//最普通的方法
return a;
}
static int add(int a,int b){//定义了与第一个方法参数个数不同的方法
return a+b;
}
}
调用add(int a):1
调用add(int a,int b):3
public class Demo {
public static void main(String[] args) {
System.out.println("调用add(int a):"+add(1));
System.out.println("调用add(int a,int b):"+add(1,2));
}
static int add(int a){//最普通的方法
return a;
}
static int add(double b,int a){//参数顺序不同
return (int)(a+b);
}
}
public class Demo {
public static void main(String[] args) {
System.out.println("调用add(int a):"+add(1));
System.out.println("调用add(int a,int b):"+add(1,2));
}
static int add(int a){//最普通的方法
return a;
}
static int add(int a,double b){//参数类型不同
return (int)(a+b);
}
}
加入出现两个参数顺序不同的同名函数,调用时会报错:
public class Demo {
public static void main(String[] args) {
System.out.println("调用add(int a):"+add(1));
System.out.println("调用add(int a,int b):"+add(1,2));
}
static int add(int a){//最普通的方法
return a;
}
static int add(double b,int a){//参数顺序不同
return 100;
}
static int add(int a,double b){//参数顺序不同
return 100;
}
}
Error:(4, 50) java: 对add的引用不明确
Demo 中的方法 add(double,int) 和 Demo 中的方法 add(int,double) 都匹配
解决(两种):
System.out.println("调用add(int a,int b):"+add(1,2.0));
System.out.println("调用add(int a,int b):"+add(1.0,2));
同一个变量,不同的方法,执行出不同的结果
class Animal {
void move(){
System.out.println("移动");
}
}
class Fish extends Animal{
void move(){
System.out.println("游动");
}
}
class Eagle extends Animal{
void move(){
System.out.println("飞翔");
}
}
public class Demo {
public static void main(String[] args) {
Animal jack = new Animal();
jack.move();
jack = new Fish();
jack.move();
jack = new Eagle();
jack.move();
}
}
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承和多态处理。在继承和多态的原理中,继承树中越是在上方的类越抽象,例如鸽子继承鸟类,鸟类继承动物类等。
创建一个Color
类,它有Red
和Blue
两个子类:
public abstract class Color {
public void show(){
}
}
class Red extends Color{
public void show(){
System.out.println("我是红色");
}
}
class Blue extends Color{
public void show(){
System.out.println("我是蓝色");
}
}
创建Demo2:
public class Demo2 {
public static void main(String[] args) {
Color c1 = new Red();
Color c2 = new Blue();
c1.show();
c2.show();
}
}
输出:
我是红色
我是蓝色
如果实例化一个c3:
public class Demo2 {
public static void main(String[] args) {
Color c1 = new Red();
Color c2 = new Blue();
c1.show();
c2.show();
Color c3 = new Color();
}
}
会报错:
错误:(8, 20) java: Color是抽象的; 无法实例化
抽象类是不能实例化的
含有抽象方法的类必须是抽象类。
含有抽象方法的类必须被继承,而抽象方法必须被抽象类的子类所重写。
如果继承于一个含有抽象方法的类,则必须实现抽象类中的抽象方法。
public abstract class Teacher {
abstract void teaching();//抽象方法不能有实体,所以直接括号结束,不能有花括号
}
class MathTeacher extends Teacher{
@Override
void teaching() {
System.out.println("Delta等于死亡。");
}
}
class EnglishTeacher extends Teacher{
@Override
void teaching() {
System.out.println("How are you? I'm fine, thank you! And you?");
}
}
public class Demo3 {
public static void main(String[] args) {
Teacher Lee = new MathTeacher();
Teacher Jon = new EnglishTeacher();
Lee.teaching();
Jon.teaching();
}
}
输出:
Delta等于死亡。
How are you? I’m fine, thank you! And you?
创建顶层父类Animal
类,两种行为,吃、繁殖:
public abstract class Animal {
public Animal(){
System.out.println("创建了一个动物");
}
public abstract void eat();
abstract public void reproduce();//两种顺序都可以
}
创建父类Bird类,具有属性羽毛,具有行为长羽毛、通过下蛋繁殖
public abstract class Bird extends Animal{
String feather;//羽毛
public Bird(String feather){
System.out.println("创建了一个鸟类");
this.feather=feather;
}
public void growFeather(){
System.out.println("长满了"+feather+"羽毛");
}
public abstract void move();
public void reproduce(){
System.out.println("下蛋");
}
}
创建子类海鸥,创建时提示自动添加方法,添加完后:
public class Seagull extends Bird{
public Seagull(String feather) {
super(feather);
}
@Override
public void move() {
}
@Override
public void eat() {
}
}
可见,第一个重写的方法来自鸟类中的抽象方法,第二个来自最顶层的动物类,说明,一个子类继承一个抽象类之后,需要实现这个抽象类以及他的父类的所有抽象方法。
完整子类的创建:
public class Seagull extends Bird{
public Seagull(String feather) {
super(feather);
System.out.println("创建了一只海鸥");
}
@Override
public void move() {
System.out.println("飞翔");
}
@Override
public void eat() {
System.out.println("吃鱼");
}
}
主方法:
public class Demo {
public static void main(String[] args) {
Seagull tom = new Seagull("白色的");
tom.eat();
tom.growFeather();
tom.move();
tom.reproduce();
}
}
输出:
创建了一个动物
创建了一个鸟类
创建了一只海鸥
吃鱼
长满了白色的羽毛
飞翔
下蛋
首先运行最顶层动物类的构造方法,然后运行鸟类的构造方法,其次运行海鸥的构造方法,再运行成员方法。
创建Chicken类:
public class Chicken extends Bird {
public Chicken(String feather) {
super(feather);
System.out.println("创建了一只鸡");
}
@Override
public void move() {
System.out.println("跑");
}
@Override
public void eat() {
System.out.println("吃米");
}
}
主方法:
public class Demo {
public static void main(String[] args) {
Chicken kaji = new Chicken("黄色");
kaji.eat();
kaji.move();
kaji.growFeather();
kaji.reproduce();
}
}
输出:
创建了一个动物
创建了一个鸟类
创建了一只鸡
吃米
跑
长满了黄色羽毛
下蛋
从抽象类中可以看出,继承抽象类的所有子类都需要将抽象类中的所有抽象方法进行覆盖。对于一些复杂的继承关系,这会造成代码的冗余,有些不需要使用父类中的某些方法的子类也需要重写这些不需要使用的方法。接口为这个情况给出了解决方案。
创建Draw
接口:
public interface Draw {
public void draw();
}
创建四边形类:
public class Qua implements Draw {//四边形
@Override
public void draw() {
System.out.println("绘制一个四边形");
}
}
创建正方形类:
public class Square implements Draw{
@Override
public void draw() {
System.out.println("绘制一个正方形");
}
}
主方法:
public class Demo {
public static void main(String[] args) {
Draw d1 = new Qua();
d1.draw();
Draw d2 = new Square();
d2.draw();
}
}
输出:
绘制一个四边形
绘制一个正方形