继承
概念:
继承就是把多个类中共同的成员提取出来,定义到某个独立的类中,
然后让这个独立类和多个类之间产生一个关系,多个类就具有独立类中的内容,它们之间的关系就是继承。
Java中继承实现
通过extends关键字
class 子类 extends 父类
//Person类
public class Person {
String name;
int age ;
String gender;
public void show(){
System.out.println("我的名字是:" + name
+ ",我的性别是:" + gender
+ ",我的年龄是:" + age
);
}
}
//Student类
public class Student extends Person {
String schoolName;
@Override
public void show() {
super.show(); //通过super访问父类的show方法
System.out.println("我的学校是:" + schoolName);
}
}
//Teacher类
public class Teacher extends Person{
String courseName;
@Override
public void show() {
super.show();
System.out.println("我教的课程是:" + courseName);
}
}
package com.day9.extend;
//Test类
public class PersonTest {
public static void main(String[] args) {
//创建学生对象
Student s = new Student();
//给属性赋值
s.name = "jack";
s.age = 20;
s.gender = "男";
s.schoolName = "南京邮电大学";
//调用方法
s.show();
Teacher t = new Teacher();
t.name = "tom";
t.age = 30;
t.gender = "男";
t.courseName = "数学";
t.show();
}
}
继承父类私有化属性
package com.day9.extend1;
public class Pet {
private String name;
private int age;
private String breeds;
private String owner;
//构造方法
public Pet() {
}
public Pet(String name, int age, String breeds, String owner) {
this.name = name;
this.age = age;
this.breeds = breeds;
this.owner = owner;
}
//set/get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getBreeds() {
return breeds;
}
public void setBreeds(String breeds) {
this.breeds = breeds;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
//普通方法show()
public void show(){
System.out.println("名字:"+name+",年龄:"+age+",品种:"+breeds+",主人:"+owner);
}
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
", age=" + age +
", breeds='" + breeds + '\'' +
", owner='" + owner + '\'' +
'}';
}
}
package com.day9.extend1;
import com.day9.extend1.Pet;
public class Dog extends Pet {
String food;
public Dog() {
}
public Dog(String name, int age, String breeds, String owner, String food) {
//这里的super表示访问父类的全参构造
super(name, age, breeds, owner);
this.food = food;
}
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public void show(){
super.show();
System.out.println("食物:"+food);
}
@Override
public String toString() {
return "Dog{" +
"food='" + food + '\'' +
"} " + super.toString();
}
}
package com.day9.extend1;
public class PetTest {
public static void main(String[] args) {
Dog d = new Dog("dd",2,"柴犬","Amy","狗粮");
//子类继承父类的的私有属性,是间接继承,通过构造方法去给属性赋值
System.out.println(d.getName());
System.out.println(d.getAge());
System.out.println(d.getBreeds());
System.out.println(d.getOwner());
System.out.println(d.getFood());
//通过父类的set方法给间接继承的私有化属性赋值
Dog d1 = new Dog();
d1.setName("11");
d1.setAge(1);
d1.setBreeds("田园犬");
d1.setOwner("Tom");
d1.setFood("狗粮a");
System.out.println(d.getName());
System.out.println(d1);
}
}
继承的好处
1.提高代码的复用性和可维护性
2.继承让类和类产生了关系,是多态性的前提
继承的弊端
1.增加了类的耦合性(耦合:类和类之间的关系),某个类改变的话,也会改变其他类,
将来程序设计的原则是:低耦合、高内聚(内聚:某个类解决问题的能力)
2.打破了封装性
继承的特点
1.Java中继承只能是单继承,只能继承一个类,但是可以多重继承
2.如果没有继承,那么系统会自动的让这个类继承Object类
3.子类不能直接继承父类的私有成员属性,但是可以通过构造、继承父类的set方法,间接的使用父类的私有成员属性
4.子类不能继承父类的构造方法,但可以通过super去访问父类的构造方法
什么时候使用继承?
如果一个类和另一个类有is-a的关系,就可以让他们产生继承
继承中的成员关系及访问特点
1.成员属性
子类属性和父类属性不同名,调用谁,就找谁的
子类属性和父类属性同名,优先访问子类的,可以同super访问父类的非私有属性
2.成员方法
子类有,父类也没有,子类将来访问子类的
子类有,父类也有,子类将来访问子类的
子类没有,父类有,就访问父类的
3.构造方法
子类构造方法会默认访问父类的无参构造
如果父类没有无参构造,怎么继承访问?
1)父类没有无参构造,那么必定存在有参构造,这个时候,可以在子类中,通过super调用父类的有参构造
2)在子类中通过this关键字,调用自己的构造方法,被调用的自身的构造方法中,必须要调用父亲的构造
package com.day9.extend2;
//父类
public class Father {
//父类的成员属性
public int number1 = 10;
int number2 = 20;
protected int number3 = 30;
private int number4 = 40;
int number5 = 50;
// public Father() {
// System.out.println("这是父类的无参构造!");
// }
public Father(int number1) {
this.number1 = number1;
System.out.println("父类的有参构造");
}
public void say(){
System.out.println("这是父类的say方法");
}
public void hello(){
System.out.println("这是父类的hello方法");
}
}
//子类
public class Son extends Father {
public int number1 = 100;
int number2 = 200;
protected int number3 = 300;
private int number4 = 400;
public Son() {
super(10);
System.out.println("子类的无参构造!");
}
public Son(int number1) {
// super(20);
this();
this.number1 = number1;
}
public Son(int number1, int number2) {
// super(30);
this();
this.number1 = number1;
this.number2 = number2;
}
public void show(){
//子类属性和父类属性名相同,
//子类访问默认是访问自己的属性
System.out.println(number1);
System.out.println(number2);
System.out.println(number3);
//子类没有number5,访问的是父类的
System.out.println(number5);
//通过super访问父类和子类同名的属性
System.out.println(super.number1);
System.out.println(super.number2);
System.out.println(super.number3);
//子类无法直接访问父类的私有属性
//System.out.println(super.number4);
}
@Override
public void say(){
// super.say();
System.out.println("这是子类的say方法");
super.hello(); //这个访问的是父类的hello方法
hello(); //这里访问的是自己类中的hello方法
}
@Override
public void hello(){
System.out.println("这是子类的hello方法");
}
}
package com.iweb.day6.test.extend2;
//测试类
public class Test {
public static void main(String[] args) {
Son son = new Son();
Son son1 = new Son(10);
Son son2 = new Son(10,20);
// System.out.println(son.number1);
// System.out.println(son.number2);
// System.out.println(son.number3);
// son.show();
// son.say();
//son.hello();
}
}
继承总结
继承关系中,被继承的类,称为父类、超类、基类,继承的类称为子类。
一个类如果没有显式的继承,默认继承object类,object类是所有父类的创始类。
类和类之间只能单继承,可以多重继承。
子类可以直接继承父类非私有属性、方法,私有属性可以间接使用,不能直接访问。
子类可以扩展父类信息,可以拥有自己的属性和方法。
继承可以提升代码复用性。
super关键字的用法
1.在子类中调用父类的属性,不能调用私有的 super.属性名
2.在子类中调用父类的方法 super.方法名()
3.在子类中调用父类的构造方法,使用super调用构造方法,必须写在构造方法中的第一行
调用无参 super();
调用有参 super(参数);
在构造方法中super是否可以与this同时出现?
不可以,super和this在构造方法中,都只能在第一行出现。
面试题:this和super的区别
1.super表示父类的对象,this表示当前类的对象
2.super在子类调用,this在当前类调用
方法重写
在继承关系中,子类重写父类的方法
规范是:
方法名相同、参数列表相同、返回值类型相同或者是父类方法返回值的子类,访问修饰符不能严于父类,不能抛出比父类方法更多的异常。
方法重写的特点
1.子类不能将父类的非静态方法,重写为静态的方法
2.子类不能重写父类的静态方法,但是可以定义和父类同名的静态方法,这个方法就是子类自己的静态方法
package com.day9.extend3;
public class Father {
//测试返回值类型
public Father show(String name){
System.out.println("show"+name);
return new Father();
}
protected void test(){
System.out.println("父类的test");
}
//父类的静态方法
public static void method01(){
System.out.println("父类的静态方法method01");
}
public void method02(){
System.out.println("父类的私有方法method02");
}
}
package com.day9.extend3;
public class Son extends Father{
//@Override注解,用来检查重写的方法是否符合规范
@Override
public Son show(String name){
System.out.println("这是子类重写父类的方法");
System.out.println("show"+name);
return new Son();
}
@Override
public void test(){
System.out.println("子类的test");
}
//子类不能重写父类的静态方法,但是可以定义和父类同名的静态方法
//这个方法是子类自己的静态方法
public static void method01(){
System.out.println("子类的method01方法");
}
//父类的私有方法无法重写
public void method02(){
System.out.println("子类私有的方法method02");
}
}
方法重写的应用
重写equals()方法
package com.day9.extend3;
import java.util.Objects;
public class Student {
private String sid;
private String name;
private int age;
private String className;
public Student() {
}
public Student(String sid, String name, int age, String className) {
this.sid = sid;
this.name = name;
this.age = age;
this.className = className;
}
//重写equals()方法
//自己写的
//instanceof 是 Java 的保留关键字。
//它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
/* @Override
public boolean equals(Object o) {
//如果比较的对象地址相同,表示他们就是同一个对象
if (this == o) {
return true;
}
//如果传进来的参数是一个学生对象
if (o instanceof Student) {
//将传进来的参数,转回Student,因为参数是Object类型
//这个时候不能直接使用,转回Student之后才能使用
Student obj = (Student) o;
if (this.sid.equals(obj.sid) &&
this.name.equals(obj.name) &&
this.age == obj.age &&
this.className.equals(obj.className)){
return true;
}else {
return false;
}
}
return false;
}
*/
//系统生成的
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
if (sid != null ? !sid.equals(student.sid) : student.sid != null)
return false;
if (name != null ? !name.equals(student.name) : student.name != null)
return false;
return className != null ? className.equals(student.className) : student.className == null;
}
@Override
public int hashCode() {
int result = sid != null ? sid.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
result = 31 * result + (className != null ? className.hashCode() : 0);
return result;
}
}
package com.day9.extend3;
public class StudentTest {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("1001","jack",20,"1");
Student s2 = new Student("1001","jack",20,"1");
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);//false
//系统类Object,有一个equals()方法,用来比较对象是否相同
//任何类,都会继承这个方法
//Object类中的,底层默认还是 ==
//如果类中想要调用equals()方法,去比较对象是否相同,必须要先重写equals()方法
System.out.println(s1.equals(s2));//false //其实还是 return s1==s2
//重写equals()方法后,返回true
String s3="hello";
String s4=new String("hello");
System.out.println(s3.equals(s4));//true
}
}
面试题:== 和 equals() 的区别
1. ==是用来比较基本类型值是否相同,引用类型的地址值是否相同
2. equals() 是Object类中的一个方法,用来比较引用类型是否是同一个对象的,但是Object类中的equals()方法底层还是==
所以,如果想要实现比较功能,将来在类中要重写equals()方法。
面试题:为什么重写equals()方法的时候同时要重写hashCode()方法
1. 一般的话,同一个对象的hashCode()值也是相同的
2. 如果只重写方法,而不重写hashCode()方法,会出现对象相同,但是hashCode()值不同的情况,这种情况一般是不允许的。
toString()方法
Object类中的一个方法,用来返回对象的字符串表示
如果直接将一个对象输出的话,默认就会以此对象,调用toString()方法,
如果没有重写toString()方法的话,对象就默认调用Object类中的toString()方法,
Object类中的toString()方法,返回的内容是
return getClass().getName() + "@" + Integer.toHexString(hashCode());
当前类的全路径名 + "@" + 对象的hashCode()值的16进制表示
重写之后,对象会调用自己类中重写的toString()方法
abstract关键字
抽象类
概念
在类的继承关系中,继承把共性的东西做了抽取,有的时候,抽取的方法相同,但是每个子类中的方法的具体实现(功能)是不一样的, 这个时候,方法相同,但是实现不同,父类中的方法就不能给出具体的实现。这种没有实现的方法,就称为抽象方法。
如果一个类中,有抽象方法,那么该类就必须要定义为抽象类
写法:通过abstract关键字修饰
public abstract class{
public abstract void method();
}
抽象类的特点
1.抽象类中可以没有抽象方法,如果有抽象方法的类,一定是抽象类
2.抽象类不可以实例化
3.抽象类子类,要么子类也是抽象类,要么子类重写父类中的所有抽象方法
抽象类中的成员特点
1.成员变量
可以有变量,也可以有常量
2.构造方法
可以有构造方法,构造方法不用来实例化,一般用来给子类访问
3.成员方法
可以有抽象方法,也可以有普通方法、静态方法
普通方法和静态方法的使用,可以和普通类的用法一样。
package com.day9.abstract1;
public abstract class Animal {
private String name;
private int age;
public Animal() {
System.out.println("父类的无参构造");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
//普通方法
//抽象方法,写法就是普通方法上,加上abstract,并且去掉方法的大括号
public abstract void eat();
//抽象类中除了抽象方法,其他的方法用法跟普通类中的继承的用法没区别
//抽象类中,可以定义普通方法
public void show(){
System.out.println("父类的普通方法");
}
//抽象类中的静态方法,可以被类名直接调用访问,
//也可以被子类继承后,子类名访问
public static void hello(){
System.out.println("父类的静态方法");
}
}
package com.day9.abstract1;
//子类继承抽象父类
//1.要么子类也是抽象类
//2.要么子类重写父类中的所有抽象方法
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
package com.day9.abstract1;
public class Test {
public static void main(String[] args) {
//创建对象
Dog dog = new Dog();
dog.eat();
dog.show();
dog.hello();
Animal.hello();
Dog.hello();
//创建Animal对象
//抽象类不能被实例化
//Animal animal = new Animal();
}
}