继承的定义
1.什么是继承?
所谓继承就是在原有类的基础之上派生出一个新的类的,这个过程称之为继承
原有类:父类
新的类:子类
---------------------------------------------------------------------
2.那继承有什么用呢?
1.可以减少代码冗余,提高开发效率
2.提高程序的可扩展性
3.因为有了继承才有了后面的多态
3.特点:
子类一旦继承了父类,就会拥有的父类的内容(属性和方法)
注意:
<1>.子类无法继承父类的构造方法
<2>.子类无法继承父类的私有属性和方法(不全面,受到访问修饰符权限的限制)
子类可以通过继承的setter()和getter()方法来操作属性。
<3>.如果2个类在不同包下,使用的是默认修饰符修饰的属性或者方法也无法继承(访问权限的影响)
<4>.子类虽然无法继承父类的构造方法,但是可以调用父类的构造方法,但是必须在子类的构造方法中的第一行。
继承的代码实现:
继承的关键字:extends
1.设计父类
Person (将学生和老师的一些公共的部分全部封装到父类中)
public class Person {
private int id;
private String name;
private int age;
private char sex;
private String phoneNum;
private String address;
...
}
Student (独有的属性或在方法)
public class Student extends Person{
...
}
测试:
public static void main(String[] args) {
Student stu=new Student();
stu.setId(1);
stu.setName("张三");
stu.setPhoneNum("111");
stu.setAddress("浦东新区");
stu.setAge(24);
stu.setSex('男');
stu.show();
}
记住:
1.创建对象的时候,如果没有改写构造方法,那么系统会默认调用默认构造(无参构造),一旦改写了构造方法,默认构造失效,将无法使用(为了防止默认构造失效,一般会给类提供一个无参构造,然后再去改写)
2.实际上在子类的构造方法的第一句系统会默认调用父类的默认构造(无参构造),当然也可以在子类的构造方法中调用改写之后的父类构造。super(...)
Teacher(独有的属性或者方法) 省略
扩展1:针对属性而言 子类继承了父类属性,但是如果在子类中又重新定义了这些被继承的属性,那么被继承的属性将会被隐藏。(遵循就近原则)
<1>.设计一个Pet类(id,nickName,age,health(1-100))
设计一个Dog类 show() 打印详细信息
设计一个Cat类 show() 打印详细信息
继承的方式
Java类只支持单继承?(一个子类只能有一个父类)
为什么?
子类一旦继承父类 那么就会拥有父类的方法或者属性,不仅如此在子类的构造方法的第一句会调用父类的构造方法。
假设一个子类拥有多个父类,那么子类构造方法的第一句到底调用哪个父类的构造方法?
public class A{
public void method(){
...
Sout...(A...);
}
}
public class B{
public void method(){
...
Sout...(B...);
}
}
假设可以出现多个父类
public class C extends A,B{
}
注意:1个子类虽然只能有一个父类,但是1个父类可以有多个子类。
Java类只支持单继承(正确)
Java只支持单继承(错误)
Java支持单继承(正确)
Java类支持多层继承:最终子类拥有整个继承体系中所有的内容(所能够继承的内容)
扩展:Java;类个顶级父类Object类(它是所有类的父类)
this关键字
this:当前类的对象 (对象的地址)
User (id,name)
User user=new User(1,"jack");
super关键字
super:父类对象 (指向父类的对象的地址)
Person(phoneNum) 父类
User (id,name) extends Person 子类
User user=new User(1,"jack"); 创建了一个子类
user.setPhoneNum("110");
this和super内存图解
this:当前类的对象
<1>.通过this去调用属性或者方法,先去子类中寻找属性或者方法,找到了指向找到的属性或者方法,找不到就会到父类中寻找。
super:父类对象
<2>.直接到父类中寻找相应的属性或者方法
重写
重写是针对方法
1.重写的概念:在子类中将继承自父类的方法进行重新改写的过程,称之为重写。
2.重写的前提条件:必须要有继承关系
3.特点:子类一旦重写了父类的方法,那么子类对象调用方法的时候,调用的重写之后的方法,父类方法将会被隐藏。
4.重写与重载之间区别:
重载:
<1>.在同一个类中,存在多个方法名字相同。
<2>.重载与访问修饰符无关
<3>.返回值可以不同。
<4>.参数必须不同
重写:
<1>.在继承关系中的子类中,将继承自父类中的方法进行重新改写的过程。
<2>.子类的访问修饰不能严于父类(子类的访问修饰符不能比父类小)
<3>.返回值必须与父类属于同类型
<4>.参数必须相同。
----------------------------------------------------------------------------------
<1>.设计一个Pet (show(),eat(),sound())...
设计一个Cat类以及一个Dog类完成方法重写。(打印的内容自己定义).
重写的内容补充
重写Object中一些常用方法:
toString();
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", userPass=" + userPass + "]";
}
equals()与hashcode()方法:
重写equals方法就必需重写hascode方法:
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
result = prime * result + ((userPass == null) ? 0 : userPass.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
if (userPass == null) {
if (other.userPass != null)
return false;
} else if (!userPass.equals(other.userPass))
return false;
return true;
}
------------------------------------------------------------------------------------
hashCode相等2个对象 equals不一定相等
equals相等2个对象 hashcode一定相等
HashSet(不可重复)
1.先判断hashCode是否相等,如果hashcode相等,判断equals是否相等
2.hascode相等,判断equals有可能不相等...
final 关键字
1.fianl关键字修饰变量那么这个变量将会变成一个常量(不可以修改的数据)
一般与static合起来用。 (命名规则:字母全部大写,单词与单词之间用_隔开)。
final static int DAY_WEEK=10;
2.final关键字修饰方法,说明方法不可以重写
3.final关键字修饰类,说明这个类不能被继承 (不能做父类了)
向上转型与向下转型
向上转型:(默认转换)
小的类型转大的类型(范围小的--->范围大的)===>(子类--->父类)
Pet pet=new Cat();
//1.注意点: 转型之后会导致数据的丢失(将不是父类的内容全部丢失),重写自父类的内容保留下来
向下转型:(强制转换)
大的类型转小的类型(范围大的--->范围小的)===>(父类--->子类)
Cat cat=(Cat)new Pet();
instanceof 判断某个实例是否属于指定的类型,如果属性指定的类型返回true 不属于返回false
//1.向上转型 (多态中应用)
Pet pet=new Cat();
pet.eat();
pet.sound();
//2.向下转型 (属于同一类型才能强制转换)
/*Dog dog=(Dog) pet;
dog.eat();*/
//3.注意必须属于同类型,才可以转换
Cat cat=(Cat) pet;
cat.eat();
cat.sound();
System.out.println(pet instanceof Pet);//true
System.out.println(pet instanceof Cat);//true
System.out.println(pet instanceof Dog);//false
类的初始化顺序
阶段一:代码块
public class Son{
static{
//静态代码块
}
{
//普通代码块
}
public Son(){
//构造方法
}
}
静态代码块>普通代码块>构造代码块
阶段二: 熟悉、方法和代码块
public class Son{
int age; //普通属性
static String name;//静态属性
public void method1(){
//普通方法
}
public static void method2(){
//静态方法
}
static{
//静态代码块
}
{
//普通代码块
}
public Son(){
//构造方法
}
}
1.静态资源(静态属性,静态代码块,谁先声明谁先加载>静态方法)>
2.非静态资源(普通属性,普通代码块,谁先声明谁先加载>普通方法)>
3.构造方法
阶段三: 父类与子类代码块
设计父类
public class Father {
static {
System.out.println("父类静态代码块...");
}
{
System.out.println("父类的普通代码块...");
}
public Father() {
System.out.println("父类的构造代码块...");
}
}
设计子类
public class Son extends Father{
{
System.out.println("子类普通代码块...");
}
static {
System.out.println("子类静态代码块...");
}
public Son() {
System.out.println("子类代码块...");
}
}
父类的静态代码块-->子类的静态代码块--->父类的普通代码块--->父类构造代码块---->子类的普通代码块
---->子类的构造代码块.
阶段四: 终极版本
父类与子类的熟悉、方法和代码块
设计子类:
public class Son extends Father{
static String pass;
int num;
{
System.out.println("子类普通代码块...");
}
static {
System.out.println("子类静态代码块...");
}
public Son() {
System.out.println("子类构造代码块...");
}
public static void method3() {}
public void method4() {}
}
设计父类
public class Father {
static String name;
int age;
static {
System.out.println("父类静态代码块...");
}
{
System.out.println("父类的普通代码块...");
}
public Father() {
System.out.println("父类的构造代码块...");
}
public static void method1() {
}
public void method2() {
}
}
父类的静态代码块,父类静态属性-->子类的静态代码块,子类静态属性
--->父类的静态方法--->子类的静态方法--->父类的普通代码块,父类的普通属性
--->父类的普通方法--->父类构造代码块---->子类的普通代码块,子类普通属性
--->子类的普通方法---->子类的构造代码块。