继承是面向对象三大特性之一,那他有什么好处呢?
1、好处
(1) 提高了代码的复用性
(2) 提高了代码的可维护性
(3) 是类中多态的前提
但是也同时存在一些弊端
2、弊端
(1) 提高了代码和代码耦合性 :事物和事物之间相互影响的程度
(2)开发的原则:高内聚,低耦合 程序设计追求 高内聚,低耦合
高内聚就是类的内部数据操作细节自己完成,不允许干涉
低耦合,仅暴露少量的方法给外部使用。
继承的好处是远远大于弊端的,继承还是要用
下面就先介绍一下继承的定义
一、继承
继承的本质是`对某一批类的抽象`,从而实现更得的建模
extends的英文意思是“扩展”。
也就是说:子类是父类的扩展。
- 继承是
类与类之间的关系
。除此之外,类和类直接的关系还有依赖,组合,聚合等。
继承关系的两个类一个为子类(也叫派生类),一个为父类(也叫基类)。 - 子类继承父类,使用关键字
extends
来表示。
子类和父类之间,从意义上应该具有“is a”的关系 - 方法重写: //@override 重写
为什么需要重写? 父类的方法功能子类不一定需要或者不一定满足
重写都是方法的重写,要有继承关系和属性无关 子类重写父类的方法
父类的引用指向了子类 (对象)
1.方法名必须相同
2.参数列表必须相同
3.修饰符 可以扩大 关键字只能是public
4.抛出的异常 只能缩小
5.方法体不同
静态方法和非静态方法区别很大
静态方法:方法的调用只和;左边的数据类型有关
非静态的方法:重写 的关键字只能是public
1、继承就是让类和类产生关系,父子关系
JAVA中的类只有单继承,没有多继承!
一个儿子只能有一个爹
2、如何让两个类之间产生关系,使用一个关键字,extends 扩展 增加 继承
3、定义一个A类,定义一个B类,让A类继承B类,此时A类中就可以拥有B类中的所有内容
4、父类和子类的关系:
(1) 父类:就是用于被继承的类,别称:超类、基类
(2) 子类:用于继承的类,别称:派生类
注意事项:
1、父类中的私有成员不能被继承(不能在子类中直接访问)
(1) 父类中的一些私有成员,不能在子类中直接调用
(2) 其实在子类对象中,仍然包含父类这些私有的成员变量,不能直接使用这些私有的内容,需要通过公共的访问方式访问
2、父类的构造方法不能被继承
(1) 构造方法的作用:是给成员变量赋值。
- 所以父类的构造方法给父类成员变量赋值
- 子类构造方法给子类成员变量赋值
- 子类成员变量一般比父类多,所以继承下来也不好用
(2) 父类的构造方法名需要和父类类名一致,子类的构造方法名,需要和子类一致,因为类名不同,无法继承
不能继承怎么解决呢?
(3) 解决:子类无法直接继承父类的构造方法,但是可以在子类中使用特殊格式调用父类的构造方法
3、继承的设计:不要为了部分功能而定义继承
(1) 狗类:属性 姓名 性别 年龄 行为 吃 喝
(2) 人类:属性 姓名 性别 年龄 钱 行为 吃 喝 吃狗肉
(3) 定义的时候 看人类有独有的钱还有行为吃狗肉,其他狗类都有,所以定义继承关系
(4) 继承的关系描述 子类和父类关系 is a
-
僵尸 和铁桶僵尸 铁桶僵尸是个僵尸
-
学生和人 学生是个人
-
狗和哺乳动物 狗是个哺乳动物
public class Demo01 {
public static void main(String[] args) {
Zi z = new Zi("张三",11);
//父类中提供了公共的访问方式 子类中可以访问父类公共 部分的内容
System.out.println(z.getName());
System.out.println(z.getAge());
//想要修改值可以通过set方法修改
z.setName("张四");
z.setAge(44);
System.out.println(z.getName());
System.out.println(z.getAge());
}
}
class Fu{ //父类
private String name;
private int age;
public Fu() {
System.out.println("父类的空参构造");
}
public Fu(String name, int age) {
System.out.println("父类的有参构造");
this.name = name;
this.age = age;
}
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;
}
}
//子类继承父类
class Zi extends Fu{
//如果没有提供构造方法 则子类中会提供一个空参构造
public Zi(){
//子类的构造方法第一行,默认有一句 super();表示访问父类的空参构造
super();//super()这句话必须在构造方法的第一行
System.out.println("子类的空参构造");
}
public Zi(String name,int age){
//name和age是私有的,子类无法直接访问
// this.name = name;
// this.age = age;
// super(实际参数);叫做super语句 作用访问父类的构造方法
//所有的构造方法中,都会默认隐藏一句super()表示访问父类的构造方法
//随便定义一个类型,都是有父类的,最顶层的父类叫做Object
//如果手动添加任何一个super语句不会默认提供了
super(name,age);//表示访问父类的有参构造
System.out.println("子类的有参构造");
}
//子类拥有父类中的内容,方法也被继承下来
public void show(){
//父类定义的私有年龄和姓名 子类无法直接访问,所以通过公共的访问方式间接的去访问
System.out.println(getName() + getAge());
}
}
二、特点
1、java支持单继承、不支持多继承,支持多层继承
-
单继承:一个子类只能有一个父类(一个孩子只能有一个亲爹)
-
多继承:一个子类可以同时拥有多个父类(多个亲爹) 不可以
2、多层继承:
(爹还有爹,爷爷还有爹)
A类继承B类,B类继承C类,那么A类中就拥有B类中的内容,也拥有C类中的内容,越顶层的类,拥有的功能就越少,主要是公共的属性和行为,越底层的类,功能就越多,有很多属性和行为,以后学习某个体系要从最顶层的类开始学,再学底层的类,这样只需要看子类特有的内容即可
3、java不支持多继承的原因:
如果可以多继承,那么子类在调用多个父类中同名的方法,父类们对这个方法有很多不同的实现,子类继承就会不知道该调用哪个父类的方法。所以不能
代码演示:
public class Demo02 {
public static void main(String[] args) {
C c = new C();
c.sayHello();
c.sayHi();
}
}
//爷爷类
class A{ //A是爷爷。B是爸爸。C是孙子
public void sayHi(){
System.out.println("孙子 你好啊");
}
}
class B extends A{
public void sayHello(){
System.out.println("儿子你好啊");
}
}
class C extends B{
}
三、继承中成员变量的关系
1、子类中定义和父类不同名的成员变量,在子类中既可以使用父类的成员变量,也可以使用子类的成员变量
2、子类和父类中定义同名的成员变量,子类就会根据就近原则,只能访问到子类自己定义的成员变量
解释一下上面两句话:子类访问某个变量-->
优先会在自己的方法中寻找,有没有定义这样一个变量,找到了就用,找不到去自己的类中去查找,找不到去父类中找,父类中没有,就继续向上寻找,一直找到所有类的根类,Object,有就使用,没有就报错
五、this和super
super
和this
都可以访问成员变量,成员方法。也都可以在构造方法中使用,且
只能出现在构造方法的第一行,用来访问构造方法的内容 .
(1) this(实际参数);访问子类的构造方法
(2) super(实际参数);访问父类的构造方法
1、this和super解释说明: 一个代表当前的 丨 另一个代表父类
(1) this表示本类当前对象的引用
(2) super表示本类当前对象父类的引用
构造方法:
this本身调用者这个对象 本类的构造
super代表父类对象的应用 父类的构造
2、总结:
(1) this既可以访问父类的内容,也可以访问子类的内容
(2) super只能访问父类的内容
super类 对比 this
调用父类的构造方法,必须出现在子类方法或者出现在构造方法的第一个
super和this不能同时调用构造方法
前提
:this没有继承也可以使用
super只能在继承条件下才可以使用
public class Demo03 {
public static void main(String[] args) {
Zi z = new Zi();
z.test();
System.out.println(z);
}
}
class Fu{ //定义父类
int i = 10;
}
class Zi extends Fu{ //定义子类继承父类
int i = 100;
int m = 300;
public void test(){
int i = 20;
System.out.println(i);//根据就近原则访问的是20
System.out.println(this.i);//加上this的一定是成员
//this既可以访问子类的成员变量,也可以访问父类的成员变量 也就是100
System.out.println(super.i);//访问父类的i 也就是10
System.out.println(this.toString());//表示本类当前对象的引用
System.out.println(super.toString());//toString可以输出对应的信息
System.out.println(super.m); //这行运行会报错,因为只能访问父类中定义的内容 子类的m没法访问
}
}
六、继承中构造方法的关系
public class Demo04 {
public static void main(String[] args) {
Zi z = new Zi();
}
}
class Fu{ //定义父类
public Fu(){
System.out.println("父类的空参构造被调用了");
}
}
class Zi extends Fu{ //同样是定义子类继承父类
public Zi(){
//默认隐藏了一句super语句 super() 表示访问父类的空参构造
this("李四");// this("李四"); this语句和super语句只能放在构造方法的第一行
System.out.println("子类的空参构造");
}
public Zi(String name){
// super(name);//表示访问父类的有参构造
// this();不能递归调用
System.out.println("子类的有参构造");
}
}
1、在初始化子类数据之前,必须对父类数据进行初始化(因为在初始化子类的时候,有可能会用到父类的数据,所以先把父类初始化完成)
2、如果在子类的构造方法中,没有显式的调用任何构造方法,此时在构造方法的第一行,系统会默认加上一句super() 默认访问父类的构造方法
3、如果在子类自己的构造方法中,显式的调用了任何构造方法,此时系统就不会再提供任何构造方法的调用
4、构造方法不能递归调用:不能自己调用自己,也不能间接的访问自己
5、this语句和super语句只能出现在构造方法的第一行,而且只能出现一个
七、继承中成员方法的关系(重写
)
1、方法名不同:子类既可以访问父类,也可以访问子类
2、方法名相同:实现子类的方法 java把这个叫做方法的重写
(1) 重载:在同一个类中,方法名相同,参数列表不同,与返回值类型无关
(2) 重写:在父子类中,方法名一样,参数列表一致,返回值类型一致,实现体不同
- 通过@Override 检测一个方法是否是重写父类的方法
- 是重写就不会报错 如果不是就报错
3、重写的方法本身还是属于父类,只不过在子类中重新实现了这个方法
4、重写的注意事项:
-
私有的方法不能被重写,子类可以定义一个和父类私有方法同名的方法,不算重写,只能算重新定义了一个方法
-
方法在重写的时候,权限不能越来越小,子类应该是越来越强大的,功能应该越来越多,不能将父类中的内容重写没了
八、补充一个代码块的知识点
1、根据位置的不同,有不同的名称,有不同的作用,有不同的执行时机
2、分类:
(1) 局部
代码块
(2) 构造
代码块
(3) 静态
代码块
1.局部代码块
1、格式:使用大括号包括起来一段代码 位置在方法中
2、作用:
(1) 限制变量的生命周期
(2) 在局部代码块中定义的变量,只能在局部代码块中使用,一旦出了代码块就不能再访问了
(3) 某个变量只需要使用一次,后续就不再使用了,就可以使用局部代码块声明,从而节省内存空间
public class Demo01 {
public static void main(String[] args) {
int a = 10;
int c;
{
//局部代码块 限制变量的生命周期,限制到当前大括号声明的位置开始,到大括号结束
int b = 20;
a = 20;
System.out.println(a);
c = 30;
System.out.println(c);
System.out.println(b);
}
System.out.println(b);
System.out.println(c);
System.out.println(a);
c = 30;
}
static int b;
// static int c;
}
2.构造代码块
1、格式:使用大括号包括起来的代码 位置在类中方法外
2、作用:给成员变量赋值
3、构造代码块的执行:
(1) 在创建对象的时候调用
(2) 在构造方法之前执行
(3) 无论你调用哪个构造方法,都会先执行一次构造代码中的内容
5、适用于,如果一个类的每一个构造方法,都需要执行同一个相同的内容,就可以提取公共的部分到构造代码块中
public class Demo02 {
public static void main(String[] args) {
Man m = new Man("小明",22);
Man m2 = new Man("小明");
Man m3 = new Man();
}
}
class Man{
private String name;
private int age;
//构造代码块,定义在类中方法外
{
//构造代码块执行 早于构造方法执行
//如果构造方法都需要执行同一个内容,就可以提取到构造代码块中
System.out.println("我是个男人,我被创建了");
}
public Man(String name) {
this.name = name;
System.out.println("一个参数的构造执行了");
}
public Man() {
System.out.println("空参构造执行了");
}
public Man(String name, int age) {
System.out.println("两个参数的构造方法执行了");
this.name = name;
this.age = age;
}
}
3.静态代码块
1、格式:
static{
}
2、类中方法外
3、作用:
(1) 用于给静态的属性赋值
(2) 用于给那些只需要执行一次的代码,数据库的驱动类加载
4、执行的特点:
(1) 执行的时机早,早于对象所有的内容,随着类的加载而加载
(2) 由于.class只需要加载一次,所以静态代码块也只要加载一次
public class Demo03 {
public static void main(String[] args) {
System.out.println(Car.number);
System.out.println(Car.number);
}
}
class Car{
//轮胎个数
static int number;
//静态代码块只要访问类就会 执行该内容
static {
number = 4;
//作用主要是为了给静态变量赋值,
System.out.println("只能执行一次");
}
}