继承
1.概念:
让类和类之间产生关系(父子关系),子类可以直接使用父类重中的非私有的成员。 子类直接继承父类,称为直接父类,间接继承的类称之为间接父类。
代码展示:
class Person { // 父类 基类 超类
public int age;
public String name;
public void show(){
System.out.println("父类方法");
}
}
class Student extends Person { // 子类 派生类
}
class Demo {
public static void main(String[] args){
Student stu = new Student();
System.out.println(stu.age); // 0
System.out.println(stu.name); //null
stu.show(); // 父类方法
}
}
2.格式:
public class 子类名 extends 父类名{}
子类也叫派生生类,subclass
父类,超类,基类,superclass
如果我么没有显示的继承一个父类的话,这个类就继承了java.long.Object类,因此所有的类都直接或者间接的继承了Object类。Object就是一个根父类
3.继承的好处和弊端
继承的注意事项: 子类继承了父类,是不能使用父类的私有内容,私有的内容是被父类继承到了但是因为权限的问题,private只能在本来中使用
好处:
提高了代码的复用性(多个类相同的成员可以放到同一个类中) 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
让类和类之间产生了关系,是多态的前提
弊端:
- 继承是侵入性的
- 降低 了代码的灵活性
- 曾强了代码的耦合性
耦合)代码与代码之间的关联可以称之为”耦合“
什么时候用继承?
当类和类之间存在共性的内容,并且产生了is a的关系,就可以考虑使用继承,来优化代码eg:水果和苹果,猫和动物
继承的特点
java中只支持单继承(一个儿子只能有一个爹),不支持多继承,但 是支持 多层继承
Java为何不支持多继承? 为了避免Java中的逻辑冲突
代码:
不支持多继承
class Fu1 {
public void show(){
System.out.println("fu1");
}
}
class Fu2 {
public void show(){
System.out.println("fu2");
}
}
class Zi extends Fu1, Fu2 {//编译报错
}
class Demo {
public static void main(String[] args){
Zi z = new Zi();
z.show(); // 调用哪一个啊??
//矛盾了 z类不知道自己调用哪个父类的show方法。
//所以 java不支持多继承
}
}
class ZengYeYe {
public void show(){
System.out.println("ZengYeYe");
}
}
class YeYe extends ZengYeYe{
public void show(){
System.out.println("YeYe");
}
}
class Fu extends YeYe{
public void show(){
System.out.println("fu");
}
}
class Zi extends Fu {
}
class Demo {
public static void main(String[] args){
Zi z = new Zi();
z.show(); // fu
// 永远使用最近的那一个
// 就近原则
}
继承中成员的访问特点
成员变量
代码:
class Fu {
int a = 5;
int b = 4;
int c = 2;
}
class Zi extends Fu {
int a = 10;
public void show(){
int c = 3;
int a = 20;
System.out.println(a); //20
System.out.println(this.a);//10
System.out.println(super.a); //5
System.out.println(b); //4
System.out.println(this.b); //4
System.out.println(super.b); //4
System.out.println(c); //3
System.out.println(this.c); //2
System.out.println(super.c); //2
}
}
在子类方法中访问一个变量的执行顺序
- 在子类局部范围找
- 在子类成员范围找
- 父类成员范围找
Super关键字
super关键字的用法和this相似
super:代表父类存储空间的标识,(不可以单独存在)可以理解为父类对象引用(this关键字指向调用该方法的对象,一般我们是在当前类中使用this关键字,所以我们常说this代表当前对象)
class Fu {
}
class Zi extends Fu {
public void show(){
System.out.println(this);
}
public void method(){
System.out.println(super); // 编译报错 super就不允许打印 因为super不是对象。
//super它只是父类的存储空间的一个标识
}
}
class Demo {
public static void main(String[] args){
Fu f = new Fu();
System.out.println(f); //0x001
Zi zi = new Zi();
System.out.println(zi); //0x002
zi.show();
zi.method();
Super内存图
this 和Super的区别
关键字访问成员变量访问构造方法访问成员方法thisthis.成员变量(访问本地类员变量)this(…)访问本类构造方法this.成员方法(…)访问本类成员方法supersuper.成员变量(访问父类成员变量)super(…)访问父类构造方法super.成员方法(…)访问父类成员方法
this()和super()必须放在代码的第一行
案例1:
class Demo {
public static void main(String[] args){
//System.out.println(super.a); //编译报错
/*
这里的编译报错有两个原因:
1.super是父类的存储空间的标,而当前类默认继承了Object类,而Object这个类里面没有a这个变量。
2.this,super关键字是不能放在静态方法里面的,因为静态方法先执行,是在
创建对象之前的,而this指的是当前对象,super是对象里面父类的存储空间的一个
标识,this都用不了,更别谈super了。
*/
}
}
案例二:
class YeYe{
int a = 5;
}
class Fu extends YeYe{
int a = 10;
}
class Zi extends Fu {
int a = 20;
public void show(){
int a = 30;
System.out.println(a); // 30
System.out.println(this.a); //20
ystem.out.println(super.a); //10
//如果你想调用YeYe的a呢:
//System.out.println(super.super.a); //编译报错
System.out.println(new YeYe().a);//只能通过new对象来调用
//YeYe y = new YeYe();
//System.out.println(y.a);
}
}
成员方法
案例1
class Fu {
public void show(){
System.out.println("aa");
}
}
class Zi extends Fu {
}
class Demo {
public static void main(String[] args){
Zi zi = new Zi();
zi.show(); // aa
}
}
案例二
通过super关键字来调用父类的方法
class Fu {
public void show(){
System.out.println("aa");
}
}
class Zi extends Fu {
public void show(){
System.out.println("bb");
}
//super.show(); // 除了定义变量的语句 可以写方法外面 其他所有的语句都要写在方法里面。
public void method(){
super.show();
}
}
class Demo {
public static void main(String[] args){
Zi zi = new Zi();
zi.show(); // bb
zi.method(); //aa
}
}
继承中构造方法的访问特点
子类中所有的构造法方法默认会访问父类中无惨构造方法 原因:
因为子类会继承父类中的数据,可能还会使用父类中的数据,所以,子类初始化之前,一定要先完成父类的初始化
每一个子类构造方法的第一条语句默认是:super(); 如果父类中没有无惨构造方法,只有带构造方法,怎么办?
通过supper关键字显示的调用父类的带惨构造方法或者在父类中提供一个无惨构造方法,反正无论如何在子类初始化之前一定要给父类尽心初始化
推荐自己给出无惨构造方法
案例1:
public class Fu {
public void show(){
System.out.println("Fu中show方法被调用");
}
}
class Zi extends Fu{
public void show(){
//虽然这里调用show()的时候是子类的shou,但是子类show()中调用了父类的show
super.show();
}
}
案例2:(super必须放在第一句)
class Fu{
public Fu(){
System.out.println("Fu类构造方法");
}
}
class Zi extends Fu{
public Zi(){
System.out.println("zi类构造方法");
// super(); // 编译报错。 因为 super() 访问构造方法的语句 必须放在第一行,目的是为了 在子类进行初始化之前 先去初始化父类
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
/*打印内容:
Fu类构造方法
zi类构造方法
*/
}
案例3:
class Fu{
// 当时学习构造方法的时候提过: 当一个类中 给出了任何的构造方法,系统就不再继续提供默认的无参构造。 所以Fu中没有无参构造
public Fu(int a){
System.out.println("Fu类有参构造方法");
}
}
class Zi extends Fu{
public Zi(){ //编译报错 , 因为父类中,没有无参构造,但是你依然用 super() 来访问父类的无参构造。
// 这儿有一个默认的 super();
System.out.println("zi类构造方法");
}
public Zi(int a){ //编译报错
// 这儿有一个默认的 super();
System.out.println("zi类有参构造方法");
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
}
}
案例4:
class Fu{
public Fu(int a){
System.out.println("Fu类有参构造方法");
}
}
class Zi extends Fu{
public Zi(){
super(10); // 编译成功了, 这是在手动的访问父类的有参构造
System.out.println("zi类构造方法");
}
public Zi(int a){
super(10);// 编译成功了, 这是在手动的访问父类的有参构造
System.out.println("zi类有参构造方法");
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
/*打印内容:
Fu类有参构造方法
zi类构造方法
*/
}
}
案例5:
class Fu{
public Fu(int a){
System.out.println("Fu类有参构造方法");
}
}
class Zi extends Fu{
public Zi(){
this(10); // 访问子类的有参构造。 编译成功,访问其他构造方法,其他构造方法的第一行也是在访问父类的构造方法。所以可以做到 子类初始化之前 先初始化父类。
System.out.println("zi类构造方法");
}
public Zi(int a){
super(10);// 编译成功了, 这是在手动的访问父类的有参构造
System.out.println("zi类有参构造方法");
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
/*打印内容:
Fu类有参构造方法
zi类有参构造方法
zi类构造方法
*/
}
}
案例6:
class Fu{
public Fu(int a){
System.out.println("Fu类有参构造方法");
}
}
class Zi extends Fu{
public Zi(){
super(10); //
this(10); // 编译报错 , 因为给父类初始化 仅仅只能初始化一次。而上一条语句已经访问了一次父类构造方法了,我所以 super() 和this() 这种的不能共存。
System.out.println("zi类构造方法");
}
public Zi(int a){
super(10);// 编译成功了, 这是在手动的访问父类的有参构造
System.out.println("zi类有参构造方法");
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
}
}
方法的重写
概述:子类中出现了和父类中一模一样的方法声明。(方法名相同,返回值相同,参数列表相同,子类的权限必须大于等于父类)
应用场景:在子类中出现了和父类相同的特性·和功能,但是子类又有自己独特的功能
重载:在同一个类中方法出现了方法名相同,参数列表不同,和返回值无关的方法,这些方法是重载的。
11.2 @Override 这是一个注解,可以帮我们检查重写方法的方法声明的正确性
11.3注意事项:
1,父类中的私有方法不能重写(父类私有方法是继承到了但是不能重写)
因为被private修饰的方法只有本类才可以访问,子类根本就看不到,所以根本就不算重写。 2,父类
静态方法,子类不能重写(通过多态可以出,即使子类的方法声明和父类的一模一样,也不算重写),父类非静态的方法,子类也必须通非静态
态方法进行重写
案例说明:
class Computer{
public static void show(){
System.out.println("Fu..........");
}
}
//鼠标
class Mouse extends Computer{
public static void show(){
System.out.println("Zi..........");
}
}
//测试类
class ComputerText{
public static void main(String[] args) {
Computer computer=new Mouse();
computer.show();//Fu
}
}
3,子类重写父类非当法时。子类的权限必须大于等于父类(public>默认>private) 被final修饰的方法不能被重写。
java中只支持单继承,不支持多继承,但是Java中支持多层继承
案例1:
class Fu {
private void show(){//被privat修饰的方法是不可以被重写的
System.out.println("fu");
}
}
class Zi extends Fu {
/*
public void show(){ // 不会报错 , 正确的。 子类 根本就看不到父类里面有show方法, 他不知道这个事儿, 然后自己写了show方法。
System.out.println("zi");
}
*/
@Override // 编译报错 因为 Override这个注解的意思是标志着 底下的这个方法是重写父类的方法。
public void show(){
System.out.println("zi");
}
}
案例二:
//A包下
public class Fu {
void show(){ //默认的访问权限,只有同类下和同包下的可以访问
System.out.println("a");
}
}
//B包下
import A.Fu;
public class Zi extends Fu {
@Override // 编译报错 zi 根本就看不到 Fu里面有 show , 自己写一个自己的show 没问题啊。
public void show(){
System.out.println("b");
}
}
案例三:父类中被static修饰的方法不能重写
class Fu {
public static void show(){
System.out.println("fu");
}
}
class Zi extends Fu {
@Override // 编译报错 因为 Override这个注解的意思是标志着 底下的这个方法是重写父类的方法。
public static void show(){
System.out.println("zi");
}
/*
public static void show(){ //正确
System.out.println("zi");
}
*/
}
12,final修饰符:
final关键字具有特殊的意思,可以修饰成员变量,成员方法,类。
final修饰的特点:
修饰方法:表明该方法是最终方法,不能被重写
修饰变量,表明该变量是常量,不能再次被赋值
修饰符:表明该类最终类,不能被继承
final 修饰局部变量
变量是基本类型:final修饰是基本类型的数据值不发生改变
变量是引用类型:final修饰值的是引用类型的地址值不发生改变,但是引用所指向的对象里面的内容是可以改变的.
上代码:
public class Text01 {
/*
* final修饰局部变量:
* */
public static void main(String[] args) {
//修饰基本数据类型:
final int a=10;
a=23;//编译报错
final Text02 text02=new Text02();//text02对象用final修饰,地址值不可变,但是对象中的内容可以修改
text02.age=30;//编译通过,
System.out.println(text02.age);
//地址不可变:
text02=new Text02();//编译报错
}
}
class Text02{
int age=20;
}
方法的重写的拓展
重写父类的方法 方法声明必须一模一样。 其实返回值可以不一样。 这个不一样,也是有关系的 子类方法的返回值类型 必须 是
父类方法返回值类型的子类。
class Computer{
public Object show(){
return null;
}
}
//鼠标
class Mouse extends Computer{
public String show(){
return null;
}
}
综合案例:
public class Animal {
public static void main (String args[]){
System.out.println("------SubClass 类继承------");
SubClasssc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自动调用父类的无参数构造器
System.out.println("-------------");
System.out.println("SubClass");
}
public SubClass(int n){
//super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
public class SubClass2 extends SuperClass{
private int n;
SubClass2(){
//super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自动调用父类的无参数构造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
执行------------------------------------------------------------------------------------------------------
------SubClass 类继承------
SuperClass()
-------------
SubClass
SuperClass()
SubClass(int n):100
------SubClass2 类继承------
SuperClass()
SubClass2
SuperClass()
SubClass2(int n):200