各位看官早安午安晚安呀
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦大家好我们今天来学习java面向对象的三大特性之一的继承
那大家肯定要问
- 什么是继承?
- 继承有什么用?
- 以及继承的语法格式是设么样的?
接下来跟着小编我带领大家一一解答!!
1:什么是继承以及作用?
继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类(也称子类) 。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
继承主要的作用就是:共性的抽取,实现代码复用
举个小例子:
猫和狗都是动物,他们都有动物的特点,譬如(名字,年龄,会跑等等),所以我们在写猫和狗这些类时就不必要再多写他们都有的特点了,直接再写一个Animal的类,让猫和狗都可以用Animal里面的成员和方法(这就是继承),然后再在他们自己的类里面加上他们自己的属性。

Dog
和
Cat
都可以继承
Animal
类,继承之后,其中:
Animal类称为父类/基类或超类
,
Dog
和
Cat
可以称为
Animal
的子类/
派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
2:继承的语法
在
Java
中如果要表示类之间的继承关系,需要借助
extends
关键字,具体如下:
修饰符 class 子类 extends 父类 {// ...}
class Dog extends Animal{
public void wangWang(){
System.out.println(name +"正在旺旺");
}
}
但是这种不建议继承;
class Robot extends Animal (机器人这种继承动物就太牵强了,我们不建议)
具体大家可以看代码实现:
class Animal{
String name;
int age;
public void eat() {
System.out.println(name + "正在吃饭");
}
}
class Cat extends Animal{
public void miMi(){
System.out.println(name +"正在喵喵");
}
}
class Dog extends Animal{
public void wangWang(){
System.out.println(name +"正在旺旺");
}
}
然后我们就可以通过子类调用父类的方法和变量了
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺仔";
dog.eat();
Cat cat = new Cat();
cat.name = "咪咪";
cat.eat();
}
扫描二维码关注公众号,回复:
17438212 查看本文章

注意:1. 子类会将父类中的成员变量或者成员方法继承到子类中了2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
3:如果父类和子类具有相同的成员属性呢?
3.1:父类和子类存在相同成员变量
下列的值应该是多少呢?
class Base {
public int a = 1;
public int b = 2;
}
class Derived extends Base {
public int a = 4;// 与父类中成员a同名,且类型相同
public int c = 3;
public void method(){
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}

如果子类和父类是同名的成员变量的时候:
- 如果访问的成员变量与父类中成员变量同名,则优先访问自己的
- 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
- 如果一定要访问父类的成员变量的时候,就要用到另外一个关键字super
3.2:父类和子类存在同名的成员方法
这个和同名的成员变量不太一样,因为这个会涉及到方法的重载:
class Base {
public int a = 1;
public int b = 2;
public void func(){
System.out.println("父类的成员方法func");
}
public void func1(){
System.out.println("父类的成员方法func1");
}
public void func2(int a){
System.out.println("父类的成员方法func2");
this.a = a;
}
}
class Derived extends Base {
public int a = 4;// 与父类中成员a同名,且类型相同
public int c = 3;
public void func(){
System.out.println("子类的成员方法func");
}
public void func2(){
System.out.println("父类的成员方法func2");
}
}
public class Test {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
derived.func1();
System.out.println(derived.a);
derived.func2(100);
System.out.println(derived.a);
}
大家可以看看这段代码输出什么?
子类和父类都有func和func2,第一次derived.func调用的是子类的,但是第二次derived.func2调用的是却是父类的,因为这里构成了方法的重载
如果父类和子类同名方法的参数列表不同 ( 重载 ) ,根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错

并且你话虽然改变了a的值,却改变的是父类的,打印出来的还是4
注意:1.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。2.通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同 ( 重载 ) ,根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;
4:super关键字
由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java
提供了
super
关键字,该关键字主要作用:在子类方法中访问父
类的成员
。(其实就是一个关键字,不代表父类的引用)
(super在当前类里使用,那么这个类一定是子类)
我还是用上面的代码举例
class Base {
public int a = 1;
public int b = 2;
public void func1(){
System.out.println("父类的成员方法func1");
}
public void func2(int a){
System.out.println("父类的成员方法func2");
}
}
class Derived extends Base {
public int a = 4;// 与父类中成员a同名,且类型相同
public int c = 3;
public void func1(){
System.out.println("重写父类的成员方法func1");
}
public void func2(){
System.out.println("父类的成员方法func2");
}
public void method(){
// 对于同名的成员变量,直接访问时,访问的都是子类的
a = 100; // 等价于: this.a = 100;
b = 101; // 等价于: this.b = 101;
// 注意:this是当前对象的引用
// 访问父类的成员变量时,需要借助super关键字
// super是获取到子类对象中从基类继承下来的部分
super.a = 200;
super.b = 201;
// 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
func2(); // 没有传参,访问父类中的func2
func2(20); // 传递int参数,访问子类中的func2()
// 如果在子类中要访问重写的基类方法,则需要借助super关键字
func1(); // 直接访问,则永远访问到的都是子类中的func1(),基类的无法访问到
super.func1(); // 访问基类的func1()
}
}
最主要的就是这一点
// 如果在子类中要访问重写的基类方法,则需要借助super关键字
func1(); // 直接访问,则永远访问到的都是子类中的func1(),基类的无法访问到
super.func1(); // 访问基类的func1()
【 注意事项 】1. 只能在非静态方法中使用(就像this一样)2. 在子类方法中,访问父类的成员变量和方法 。3.super只能访问从父类那里继承过来的成员属性4.super()调用父类的构造方法(接下来讲解)
5:关于构造方法
俗话说:
父子父子,先有父再有子,即:子类对象构造时,必须要先调用父类的构造方法,然后再执行子类的构造方法。(但是我们上面的代码不是没有帮助构造还是运行起来了吗?其实是编译器帮我们写了并且隐藏了构造方法)
注意子类构造方法中默认会调用基类的无参构造方法:super()
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,// 并且只能出现一次

如果父类具有带参数的构造方法:
大家可以看到如果父类有了带参数的构造方法,子类却没有super(name)帮助父类完成构造,编译器就会报错

这个时候我们点击
Alt+Enter键(Alt+enter(双击enter)更快)就会可以让编译器帮助我们写出构造方法

解释:
子类对象中成员是有两部分组成的,父类继承下来的以及子类新增加的部分 。父子父子 肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 , 然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整
注意:
情况1: 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的 super() 调用,即调用基类构造方法情况2: 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的 父类构造方法调用,否则编译失败。3. 在子类构造方法中, super(...) 调用父类构造时,必须是子类构造函数中第一条语句。4. super(...) 只能在子类构造方法中出现一次,并且不能和 this 同时出现(因为构造方法调用其他的构造方法this(参数)也需要在第一行)
6:this和super的关系
6.1:相同点
1. 都是 Java 中的关键字2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
6.2:不同点
1:this 是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成 员的引用(这么说也不太准确)(其实super就只是一个关键字)2. 在非静态成员方法中, this 用来访问本类的方法和属性, super 用来访问父类继承下来的方法和属性3. 在构造方法中: this(...) 用于调用本类构造方法, super(...) 用于调用父类构造方法,两种调用不能同时在构造方法中出现4. 构造方法中一定会存在 super(...) 的调用,用户没有写编译器也会增加(默认增加),但是 this(...) 用户不写则没有

7:回顾代码块(学完super()调用父类构造方法之后)
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class Test2 {
public static void main(String[] args) {
Student student1 = new Student("张三", 19);
System.out.println("===========================");
Student student2 = new Student("李四", 20);
}
}
静态代码块还是最先执行,然后帮父类构造方法(先实例代码块)帮助父类构造完然后轮到子类
并且静态代码块还是只能执行一次

8.protected访问限定符
在类和对象章节中,为了实现封装特性,
Java
中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其他包中被访问。
不是说被在不同包里的子类就可以访问吗?但是为什么报警告呢?

警告显示a已经被protected修饰(我肯定知道啊)
换super.a就不会报警告了

接下来讲述一下被各个限定符修饰的成员属性:
// 为了掩饰父类中不同访问// extend01包中public class B {private int a ;protected int b ;public int c ;int d ;}// extend01包中// 同一个包中的子类public class D extends B {public void method (){// super.a = 10; // 编译报错,父类 private 成员在相同包子类中不可见super . b = 20 ; // 父类中 protected 成员在相同包子类中可以直接访问super . c = 30 ; // 父类中 public 成员在相同包子类中可以直接访问super . d = 40 ; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问}}// extend02包中// 不同包中的子类(protected还能起作用)public class C extends B {public void method (){// super.a = 10; // 编译报错,父类中 private 成员在不同包子类中不可见super . b = 20 ; // 父类中 protected 修饰的成员在不同包子类中可以直接访问super . c = 30 ; // 父类中 public 修饰的成员在不同包子类中可以直接访问//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问}}// extend02包中// 不同包中的类(不同包也不是子类)public class TestC {public static void main ( String [] args ) {C c = new C ();c . method ();// System.out.println(c.a); // 编译报错,父类中 private 成员在不同包其他类中不可见// System.out.println(c.b); // 父类中 protected 成员在不同包其他类中不能直接访问System . out . println ( c . c ); // 父类中 public 成员在不同包其他类中可以直接访问// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问}}
注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了
什么时候下用哪一种呢 ?我们希望类要尽量做到 " 封装 ", 即隐藏内部实现细节 , 只暴露出 必要 的信息(接口)给类的调用者 .因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限 . 例如如果一个方法能用 private, 就尽量不要用public.另外 , 还有一种 简单粗暴 的做法 : 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望大家能写代码的时候认真思考 , 该类提供的字段方法到底给 " 谁 " 使用 ( 是类内部自己用, 还是类的调用者使用 , 还是子类使用)
9.继承方式
java里面支持下列的继承方式:

但是 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.如果想从语法上进行限制继承, 就可以使用 final 关键字
10.final关键字:
final关键可以用来修饰变量、成员方法以及类
1.修饰变量或字段,表示常量
(
即不能修改
)
final int a = 10 ;a = 20 ; // 编译出错
2.
修饰类:表示此类不能被继承
final public class Animal {...}public class Bird extends Animal { //这里不能被继承...}// 编译出错Error :( 3 , 27 ) java : 无法从最终 com . bit . Animal 进行继我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承.
3.
修饰方法:表示该方法不能被重写
(
后序介绍
)
11.继承和组合:
组合:其实就是一种代码的实现方式(没有关键字修饰或其他的)
继承表示对象之间是
is-a
的关系
,比如:狗是动物,猫是动物
组合表示对象之间是
has-a
的关系
,比如:汽车

// 轮胎类class Tire {// ...}// 发动机类class Engine {// ...}// 车载系统类class VehicleSystem {// ...}class Car {private Tire tire ; // 可以复用轮胎中的属性和方法private Engine engine ;private VehicleSystem vs ;// ...其实就是用各种类一起为我所用}// 奔驰是汽车class Benz extend Car {// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来}
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
最后给大家提个问题:这个代码为什么会报错?
class Animal{
String s;
int a = 10;
}
class Dog extends Animal{
a = 20;
}
Java中,如果你想在子类中修改从父类继承的字段的值,你需要在子类的构造器、方法或者初始化块中进行这个操作,而不能直接赋值
使用构造方法或者构造代码块都可以
{
a = 20;
}
public Dog() {
a = 20;
}
或者提供一个setter方法
class Animal {
String s;
int a = 10;
public void setA(int a) {
this.a = a;
}
}
class Dog extends Animal {
public Dog() {
this.setA(20); // 在Dog的构造器中调用setter方法
}
}
- 直接在类体中(如我们的原始尝试)为继承的字段赋值是不允许的,因为Java不支持这种语法。
- 考虑到封装和代码的可维护性,通常推荐使用setter方法或者通过构造器来设置字段的值。
- 或者通过super访问,一般访问父类里面的元素都是通过super访问
上述就是 Java面向对象之继承的全部内容了,能看到这里相信您一定对小编的文章有了一定的认可
有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正
您的支持就是我最大的动力!!!!