Java之面向对象下

此为个人学习笔记,如有错误,欢迎指教

继承

概述:

多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承这个类即可。这个类称为父类,多个类称为子类。

格式:

class 类名 extends 父类名{}

继承的好处

提高了代码的复用性
提高了代码的维护性
使类与类之间有了关系,多态的前提

继承的弊端:

耦合性很强
  #####开发原则:低耦合,高内聚
    耦合:类与类的关系
    内聚:自己完成事件的能力
子类只能继承父类所有的非死有成员,打破了封装性

继承的特点:

只支持单继承,不支持多继承
支持多层继承(集合体系)

继承时子父类成员之间的关系:

成员变量关系:

子类的成员变量与父类的成员变量名称相同时,使用变量的原则:就近原则,使用的是子类的成员变量(与多态中的成员变量关系结合理解);

在子类方法中访问一个变量的查找顺序(就近原则):
  A.在子类方法的局部范围查找
  B.在子类的成员范围找
  C.在父类的成员找
构造方法关系:
子类中所有的构造方法(无论带参与否)默认都会访问父类的无参构造
子类中所有的构造方法的方法体第一句语句默认是super(),为了父类数据的初始化
若父类没有无参数构造,那么
  A.子类需要通过super(...)去调用父类的其他带参的构造方法
  B.子类需要使用this(...)去调用本类的其他构造方法,且这个构造方法中使用super(...)调用了父类的其他带参的构造方法;
this(...)和 super(...)第一要放在构造方法的第一句,否则多次初始化父类数据
成员方法关系:

子类的成员方法与父类的成员方法的返回值、方法名、参数列表都相同(这种情况也叫方法重写)时,使用的是子类的方法;

重写:子父类中,成员方法的返回值、方法名、参数列表都相同时,子类的成员方法覆盖父类的成员方法,调用时使用的是子类的方法体,若还想调用父类的方法体则使用super.(...);

子类重写父类方法时,子类的成员方法权限 >= 父类的成员方法权限

注意事项:

子类只能继承父类所有的非死有成员
子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
不要为了部分功能去继承

类加载时,发现其有父类先加载父类,再加载子类,静态修饰内容是随着类的加载而加载的,所以当子父类中有static时,static是最先加载的;类中有静态代码块、构造代码块、构造方法,则执行顺序是:
静态代码块>构造代码块>构造方法

看代码写结果:
 public class ExtendsDemo2 extends X{
    Y y = new Y();

    ExtendsDemo2() {
        System.out.println("Z");
    }

    public static void main(String[] args) {
        ExtendsDemo2 z = new ExtendsDemo2();
    }
}

class Y {
    Y() {
        System.out.println("Y");
    }
}

class X {
    Y y = new Y();

    X() {
        System.out.println("X");
    }
}
考点:
    类的初始化过程,
    子父类的初始化(分层初始化):先初始化父类后初始化子类
    super仅仅表示要先初始化父类数据,再初始化子类数据
结果:YXYZ
看代码写结果:
package extends demo

public class ExtendsDemo3 {

    public static void main(String[] args) {
        B b = new B();
    }
}

class A{
    static{
        System.out.println("父类  ----   静态代码块");
    }
    {
        System.out.println("父类  ----   代码块");
    }
    A(){
        System.out.println("父类  ----   构造方法");
    }
}

class B extends A{
    static{
        System.out.println("子类  ----   静态代码块");
    }
    {
        System.out.println("子类  ----   代码块");
    }
    B(){
        System.out.println("子类  ----   构造方法");
    }
}
考点:
  静态代码块>构造代码块>构造方法
  子父类:分层初始化
结果:
    父类  ----   静态代码块 //随着类的加载而加载
    子类  ----   静态代码块 //随着类的加载而加载
    父类  ----   代码块 //先初始化父类
    父类  ----   构造方法 //先初始化父类
    子类  ----   代码块 //后初始化子类
    子类  ----   构造方法 //后初始化子类
看代码写结果:
public class ExtendsDemo4 {
    
    public static void main(String[] args) {
        D d = new D();
        d.show();
    }
}

class C{
    public String name = "我是 --父类--的name";
}

class D extends C{
    public String name = "我是 --子类--的name";
    
    public void show(){
         String name = "我是 --局部变量--的name";
         System.out.println(name);
         System.out.println(this.name);
         System.out.println(super.name);
    }
}
考点:this和super
结果:
    我是 --局部变量--的name
    我是 --子类--的name
    我是 --父类--的name

多态

多态前提和提现:

有继承关系
有方法重写
有父类引用指向子类对象
      父 f = new 子();

多态时子父类成员之间的访问特点:

成员变量的访问特点:编译看左边,运行看左边
/**
 * 多态中  成员变量的访问特点:编译看左边,运行看左边
 */
public class ExtendsDemo1 {

    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(child.num2);//20
        
        Father child1 = new Child();
        //父类引用对象,取父类中的值:运行看左边
        System.out.println(child1.num2);//200 
        //报错:编译看左边,Father中无num变量,所以编译不通过
        System.out.println(child1.num1);
        
        GrandFather child3 = new Child();
        //祖父类引用对象,取祖父类中的值:运行看左边
        System.out.println(child3.num2);//2000 
    }
}

class GrandFather {
    public int num2 = 2000;
}

class Father extends GrandFather {

    public int num2 = 200;
}

class Child extends Father {
    public int num1 = 10;
    public int num2 = 20;
}
构造方法的访问特点:
创建子类对象的时候,访问父类的构造方法,对父类数据初始化
成员方法的访问特点:编译看左边,运行看右边
package duo tai
/**
 * 多态中  成员方法的访问特点:编译看左边,运行看右边
 */
public class DuoTaiDemo2 {

    public static void main(String[] args) {
        Child1 child = new Child1();
        //运行看右边:输出的是子类对象中的method方法
        child.method();//20
        child.show();//20
        
        Father1 child1 = new Child1();
        //运行看右边:输出的是子类对象中的method方法
        child1.method();//20
        //编译看左边:Father类中没有此方法,所以报错
        child1.show();
        
        GrandFather1 child3 = new Child1();
        //运行看右边:输出的是子类对象中的method方法
        child3.method();//20
    }
}

class GrandFather1 {
    public int num2 = 2000;
    public void method() {
        System.out.println(num2);
    }
}

class Father1 extends GrandFather1 {

    public int num2 = 200;
    public void method() {
        System.out.println(num2);
    }
}

class Child1 extends Father1 {
    public int num1 = 10;
    public int num2 = 20;
    public void method() {
        System.out.println(num2);
    }
    public void show() {
        System.out.println(num2);
    }
}
静态方法的访问特点:编译看左边,运行看左边
package duo tai
/**
 * 多态中  成员方法的访问特点:编译看左边,运行看左边
 */
public class DuoTaiDemo3 {

    public static void main(String[] args) {
        Child2 child = new Child2();
        //运行看右边:输出的是子类对象中的method方法
        child.method();//子类---静态
        
        Father2 child2 = new Child2();
        //运行看左边:输出的是父类对象中的method方法
        child2.method();//父类---静态
        //编译看左边:Father类中没有此方法,所以报错
        child2.show();
        
        GrandFather2 child3 = new Child2();
        //运行看右边:输出的是祖父类对象中的method方法
        child3.method();//祖父类---静态
    }
}

class GrandFather2 {
    public static void method() {
        System.out.println("祖父类---静态");
    }
}

class Father2 extends GrandFather2 {

    public static void method() {
        System.out.println("父类---静态");
    }
}

class Child2 extends Father2 {
    public static void method() {
        System.out.println("子类---静态");
    }
    public void show() {
        System.out.println("子类---静态---show");
    }
}

多态时子父类成员之间的访问特点的内存图解:

/**
 * 多态时子父类成员之间的访问特点的内存图解
 */
public class DuoTaiDemo4 {

    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.age);//40
        f.show();//子类--show
    }
}

class Fu{
    public int age = 40;
    public Fu(){}
    public void show(){
        System.out.println("父类--show");
    }
}

class Zi extends Fu{
    public int age = 20;
    public Zi(){}
    public void show(){
        System.out.println("子类--show");
    }
    public void method(){
        System.out.println("子类--show");
    }
}
多态时子父类成员之间的访问特点的内存图解.png

多态好处:

提高了代码的维护性(继承)
提高了代码的扩展性(多态)

多态弊端:

不能使用子类特有功能(编译看左边)
若要使用则需要强制转换(对象转型):
    向上转型(把子变父):Fu f = new Zi();
    向下转型(把父变子):Zi z = (Zi)f;//Zi与Fu是有关系的(子父类,接口实现类)

对象转型图解


对象转型图解

多态几种变现形式:

  A:具体类多态(开发中很少遇到)
  B:抽象类多态(常用)
  C:接口多态(最常用)

抽象类

概述:

抽象方法:不是具体的功能成为抽象的功能,那么此方法就是抽象方法
抽象类:一个类中如果有抽象方法,则该类必须是抽象类;

抽象类特点:

A:必须使用abstract关键字修饰
B:抽象类中不一定有抽象方法,但是抽象方法的类必须定义为抽象类
C:抽象类实例化是通过其子类实例化的(多态),本身不能通过new实例化,因为其不是具体的;
  抽象类不能通过new实例化,那么构造方法的意义:抽象类的构造方法是用于子类访问父类数据的初始化;
D:抽象类的子类:
    1)  如果子类不能重写抽象类的全部抽象方法,那么此子类还是抽象类
    2)  如果子类重写了所有的抽象方法,那么此子类是一个具体的类;

抽象类成员的特点:

成员变量:与普通类的成员变量一样
构造方法:用于子类访问父类数据的初始化;
成员方法:既可以是抽象的,也可以是非抽象的
    抽象方法:  强制要求子类做的事情
    非抽象方法:    子类继承的事情,提供代码的复用性

注意事项:

一个类如果没有抽象方法可以定义为抽象类吗?意义何在?
可以:作用是不让创建对象
abstract和private、final、static不能共存
private:冲突;
   private和abstact同时修饰方法修时,
   private修饰不能被继承继而不能重写,
   而abstract修饰的方法是要求必须重写的;
final:冲突;
  与private一样的道理;
static:无意义;
  当static和abstact同时修饰方法时,
  方法是没有方法体的,
  static随着类的加载而加载,加载一个没有方法体的方法,无意义;

接口

概述:

抽象方法:不是具体的功能成为抽象的功能,那么此方法就是抽象方法
抽象类:一个类中如果有抽象方法,则该类必须是抽象类;

接口特点:

A:使用interface关键字表示
      格式:interface 接口名{}
B:接口不能通过new实例化,只能通过多态来实例化
C:接口的子类:
     可以是抽象类,但没有什么意义
     可以是具体的实现类,使用implement表示 
        格式:class 类名 implement 接口名{}
     可以是接口,使用extends(java源码 interface BeanContext extends Collection)

接口成员特点:

成员变量:只能是常量,并且是静态的
          默认修饰符是:public static final 
构造方法:无
          接口实现类中的构造方法的super(),是Object的构造方法
          查看API可知Object的构造只有一个:无参数构造,所以子类的构造默认都有一个无参的super();
成员方法:
    1.8版本之前:只能是抽象方法,默认修饰符是 public abstact
    1.8版本:可以出现带方法体的方法,主要有2种:默认方法和静态方法
        /**
         * java1.8版本接口的新特性:可以定义带有方法体的方法,default和static
         */
        public class InterfaceDemo1 {
            
            public static void main(String[] args) {
                Interface1 inter = new Interface1Impl();
                inter.getNameDefault();
                inter.show();
                Interface2.getNameStatic();
            }
            
        }

        interface Interface1{
            void show();
            default void getNameDefault(){
                System.out.println("接口中的带有方法体的默认方法");
            }
        }

        interface Interface2 extends Interface1{
            void method();
            static void getNameStatic(){
                System.out.println("接口中的带有方法体的静态方法");
            }
        }
        class Interface1Impl implements Interface2 {

            @Override
            public void show() {
                System.out.println("接口实现类--show");
            }

            @Override
            public void method() {
                System.out.println("接口实现类--method");
                
            }
            
            
        }

类、抽象类、接口之间:

抽象类与接口的区别:
设计理念的区别:
    抽象类:继承关系,体现的是"is a "是的关系。
           抽象类中定义的是该继承体系的共性功能。
    接  口:实现关系,体现的是"like a "。
           接口中定义的是该继承体系的扩展功能。
成员区别:
    抽象类:
      成本变量:变量、常量都可以
      构造方法:有
      成本方法:普通方法和抽象方法都可以
    接  口:
      成本变量:只能使常量
      构造方法:无
      成本方法:1.8前只能抽象方法,1.8以后普通方法是default和static
类与类,接口与接口,类与接口各自之间的关系
类与类:继承关系,单继承可以多层继承
类与接口:实现关系,可以多实现(多个接口使用“,”隔开)
接口与接口:继承关系,单继承也可以多继承(多个接口使用“,”隔开)
类、抽象类、接口作为形式参数时:
类:传递的是该类的对象(地址值)
抽象类:需要的是该抽象类的子类对象(地址值)
接口:需要的是该接口的实现类对象(地址值)
类、抽象类、接口作为返回值类型时:
类:返回的是该类的对象(地址值)
抽象类:返回的是该抽象类的子类对象(地址值)
接口:返回的是该接口的实现类对象(地址值)

内部类:

概述:

定义在另一个类中的类

内部类的访问特点:

内部类可以直接访问到外部类中的成员,包括私有
外部类想访问内部类中的成员,需要创建内部类的对象

内部类的位置:

成员内部类:在类的成员位置
  使用: 外部类.内部类  对象名 = 外部类对象.内部类对象;
局部内部类:
    /**
     * 在内部类中访问外部类:使用  外部类.this
     */
    public class InnerDemo1 {

        public static void main(String[] args) {
            Outer.Inner inner = new Outer().new Inner();
            inner.show();
        }
    }

    class Outer{
        public int num = 10;
        class Inner{
            public int num = 20 ;
            public void show(){
                int num = 30;
                System.out.println(num);//30
                System.out.println(this.num);//20
                System.out.println(Outer.this.num);//10
            }
        }
    }

局部内部类:定义在方法内的类
    使用:可以创建内部类的对象,通过内部类对象操作内部类成员
局部内部类访问局部变量的注意事项?
    /**
     * 局部内部类访问局部变量的注意事项:局部变量需要使用final修饰
     * 1.8之前:局部内部类使用局部方法,书写时使用final修饰,
     *  编译后的class文件中使用变量位置是变量值
     * 1.8版本:局部内部类使用局部方法,书写时不使用final修饰,
     *  编译后的class文件中自动给变量加入final修饰,且使用变量位置还是变量名
     * 说明:不确定是1.7之前还是1.8之前
     */
    public class InnerDemo2 {

        public static void main(String[] args) {
            Outer1 inner = new Outer1();
            inner.show();
        }
    }

    class Outer1 {
        public void show() {
            //应该使用final修饰,但是1.8没有报错呀?
            //查看class文件发现:int num = 30; 变成了 final int num = 30;
            int num = 30;
            class Inner1 {
                public void method() {
                    /*
                     * 查看class文件发现:num还是num,而不是30;       
                     */
                    System.out.println("局部内部类--method"+num);
                }           
            }
            Inner1 inner1 = new Inner1();
            inner1.method();
        }
    }

匿名内部类

内部类的简化写法(类似匿名对象,没有名字)

匿名内部类的格式:

new 类名或接口名(){重写方法};

匿名内部类的本质是什么?

  是一个类的子类或接口实现类或抽象类的具体类的匿名对象
  在堆中开辟空间,只是这片空间没有与栈内存有相互关联;
    /**
     * 匿名内部类:是一个类的子类或接口实现类或抽象类的具体类的匿名对象
     */
    public class InnerDemo3 {

        public static void main(String[] args) {
            Outer3 out = new Outer3();
            out.method(40);;
        }
    }

    class Outer3{
        public void method(int num){
            new Inner3(){
                @Override
                public void show() {
                    System.out.println("重写 ---  show = " +num);
                }
            }.show();
            
            Inner4 i = new Inner4(){
                @Override
                public void show() {
                    System.out.println("重写 ---  show = " +num);
                }
                @Override
                public void show2() {
                    System.out.println("重写 ---  show2 = " +num);
                }
            };
            
            i.show();
            i.show2();
        }
        
    }

    interface Inner3{
        public abstract void show();
    }
    interface Inner4{
        public abstract void show();
        public abstract void show2();
    }

匿名内部类在开发中使用:

/**
 * 匿名内部类:是一个类的子类或接口实现类或抽象类的具体类的匿名对象
 */
public class InnerDemo4 {

    public static void main(String[] args) {
        Person out = new Person();
        out.method(new Inner5(){
            @Override
            public void show() {
                System.out.println("匿名内部类在开发中的常见使用" );
            }
        });;
    }
}

interface Inner5{
    public abstract void show();
}

class Person{
    public void method(Inner5 inner){
        inner.show();
    }
}
看结果写代码
/**
 * 看接口写代码
 */
public class InnerDemo5 {

    public static void main(String[] args) {
        Person1.method().show();
    }
}

interface Inner6{
    public abstract void show();
}

class Person1{
    //补齐代码
    public static Inner6 method(){
        return new Inner6(){
            @Override
            public void show() {
                System.out.println("hello world" );
            }
        };
    }
}
考点:Person1.method():方法静态
     Person1.method().show()  链式编程
     匿名内部类
参考书籍视频:
-学习视频:黑马程序员_Java基础视频-深入浅出精华版
-阅读书籍:Java核心技术 卷I(机械工业出版社)
          API1.8;;;
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容