8.面向对象

Java 的核心思想就是 OOP

一、初识面向对象

1.面向过程 & 面向对象

  • 面向过程思想(线性思维)

    • 步骤清晰简单,第一步做什么,第二步做什么……
    • 面向过程适合处理一些较为简单的问题
  • 面向对象思想:抽象

    • 物以类聚, 分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类下的细节进行面向过程的思索。
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
  • 对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

  • 属性 + 方法就是一个类

2.什么是面向对象

  • 面向对象编程(Object Oriented Programming,OOP)
  • 面向对象编程的本质就是:以类的方式组织代码,以对象的形式封装数据。(类是用来组织代码的,每一个具体的对象封装每一个具体的数据)
  • 抽象(理解:抽像(把像的那一部分抽取出来))
  • 三大特性:
    • 封装:把数据包装起来
    • 继承:父类和子类之间有一个继承关系,子类就拥有父类的所有东西
    • 多态:同一个事物会有多种形态(同样是学习,学到的东西不一样)
  • 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
    • (先有很多人,然后把人提取出共同特点,就是抽象,抽象出来为某一类,比如老师和学生,先有具体的一个一个人这个对象,然后才去抽象出来一个一个类)
  • 从代码运行角度考虑是先有类后有对象。类是对象的模板。
    • (要先把东西分好类,然后再去实现)

二、方法回顾和加深

1.方法的定义

  • 修饰符
  • 返回类型
  • break 和 return 的区别
    • break :①跳出 switch ;②结束循环
    • ( continue :结束一次循环)
    • return :方法结束,返回一个结果,返回值必须和返回值类型相同
  • 方法名:注意规范(首字母小写和驼峰命名原则),见名知意
  • 参数列表:(参数类型 参数名),一个方法可以有多个参数,可以通过 ...定义可变长参数
  • 异常抛出:ArrayIndexOutOfBoundsException :数组下标越界异常!
//类
public class DefinitionOfMethod {
    //main()方法:用来启动当前这个程序的,一个程序里面只有一个 main() 方法
    public static void main(String[] args) {

    }

    /*
    方法的定义:
    修饰符 返回值类型 方法名(参数类型 形式参数){
        //方法体
        return 返回值;
    }
     */
    // return :结束方法,返回一个结果
    public String sayHello(){
        return "hello";
    }

    //如果返回值类型是 void ,返回值可以默认不写,如果要写的话 return; 就好了
    public void hello(){
        return;  //返回空值
    }

    //比较大小
    public int max(int a, int b){
        return a > b ? a : b;
    }
}

2.方法的调用(递归)

  • 静态方法

  • 非静态方法

    public class MethodCall_StaticMethod {
        public static void main(String[] args) {
            //静态方法 static :直接通过 类名.方法名() 调用方法
            Student.say();  //学生说话
    
            //非静态方法:通过 new 实例化这个类
            //对象类型 对象名 = 对象值;
            Student student = new Student();
            student.read();  //学生读书
    
            new Student().read();  //学生读书
        }
    }
    
    //学生类
    class Student{
    
        //静态方法 static :直接通过 类名.方法名() 调用方法
        public static void say(){
            System.out.println("学生说话");
        }
    
        //非静态方法:通过 new 实例化这个类
        public void read(){
            System.out.println("学生读书");
        }
    }
    
    public class MethodCall_StaticMethod {
        //两个静态方法是可以直接调用的
        //两个非静态方法是可以直接调用的
        //静态方法无法直接调用非静态方法
        //非静态方法可以直接调用静态方法
    
        //和类一起加载的,类存在的时候就已经存在了
        public static void a(){
                b();
    //            c();  //已经存在的调用不存在的东西会报错
        }
    
        public static void b(){
    
        }
    
        //对象创建(类实例化)之后才存在
        public void c(){
                a();
                d();
        }
    
        public void d(){
    
        }
    }
    
  • 形参和实参

    public class MethodCall_FormalAndRealParameters {
        public static void main(String[] args) {
            //实际参数:真实的参数,实际参数和形式参数的类型要对应
            int add = MethodCall_FormalAndRealParameters.add(1, 2);
            System.out.println(add);
        }
    
        //形式参数:占位符,真实不存在,形式参数可以随便写,但是,真实传递进去的类型要一一对应
        public static int add(int a, int b){
            return a + b;
        }
    }
    
  • 值传递和引用传递: Java 里面都是值传递

    //值传递
    public class MethodCall_ValuePassing {
        public static void main(String[] args) {
            int i = 1;
            System.out.println(i);  //1
    
            MethodCall_ValuePassing.change(i);
            System.out.println(i);  //1
        }
    
        //返回值为空
        public static void change(int i){
            i = 10;
        }
    }
    
    //引用传递:传递一个对象,本质还是值传递
    public class MethodCall_ReferencePassing {
        public static void main(String[] args) {
            Person person = new Person();
            System.out.println(person);
            System.out.println(person.name);  //null
    
            BMethodCall_ReferencePassing.change(person);
            System.out.println(person.name);  //张三
        }
    
        public static void change(Person person){
            // person 是一个对象,指向 --> Person person = new Person(); 这是一个具体的人,可以改变属性
            person.name = "张三";
        }
    }
    
    //定义了一个 Person 类,有一个 name属性
    class Person{
        String name;  //null
    }
    
  • this 关键字

三、对象的创建分析

1.类与对象的关系

  • 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。

    • 动物(猫、狗、猪)、植物(树、花、草)、手机、电脑……
    • Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
  • 对象是抽象概念的具体实例

    • 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。
    • 能够体现出特点,展现出功能的是具体的实例,而不是抽象的概念。
  • 我们可以将这些思想转换为代码实现!

2.创建与初始化对象

  • 使用 new 关键字创建对象
  • 使用 new 关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
public class CreateObject {
    public static void main(String[] args) {
        //类是抽象的,需要实例化,变成一个对象:通过 new 关键字实例化
        //类实例化之后会返回一个自己的对象:类是抽象的,对象是具体的实例
        // zhangsan 对象就是 Student 类的具体实例
        Student zhangsan = new Student();
        Student lisi = new Student();

        zhangsan.name = "张三";
        zhangsan.age = 23;

        System.out.println(zhangsan.name);  //张三
        System.out.println(zhangsan.age);  //23

        System.out.println(lisi.name);  //null
        System.out.println(lisi.age);  //0
    }
}

//学生类:一个抽象的模板
class Student {

    //一个类里只可能存在这两个东西:属性和方法
    //属性:字段
    String name;  //默认为 null
    int age;  //默认为 0

    //方法:行为
    public void study(){
        System.out.println(this.name + "学习");
    }
}

3.构造器

  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
    1. 必须和类的名字相同
    2. 必须没有返回类型,也不能写 void
  • 构造器必须掌握
  • IDEA快捷键: alt + ins 快速生成构造器
public class Constructor {
    public static void main(String[] args) {
        //通过 new 关键字实例化一个对象,调用构造器
        //调用无参构造器
        Person zhangsan = new Person();
        System.out.println(zhangsan.name);  //张三

        //调用有参构造器
        Person lisi = new Person("李四");
        System.out.println(lisi.name);  //李四
    }
}

class Person{

    String name;

    //一个类即使什么都不写,也会存在一个方法,默认的无参构造器
    //显式的定义构造器:名字与类名相同,并且没有返回值的方法
    //构造器的作用:
    //1. 使用 new 关键字,必须要有构造器:本质是在调用构造器
    //2. 构造器用来初始化值:
    //无参构造器:定义了有参构造器,默认把无参构造器空着就行,里面什么都不用写
    Person(){
        //初始化一些信息
        this.name = "张三";
    }

    //有参构造器:一旦定义了有参构造器,无参构造器就必须显式定义,否则无参构造器就无效
    Person(String name){
        this.name = name;
    }
}
  • 总结:

    • 构造器:

      • 特点:

        1. 方法名和类名相同
        2. 没有返回值
      • 作用:

        1. new 本质在调用构造器
        2. 初始化对象的值
      • 注意点:

        1. 定义了有参构造器之后,如果想使用无参构造器,必须要显式的定义一个无参构造器

4.创建对象内存分析

8.面向对象

public class CreateObjectMemoryAnalysis {
    public static void main(String[] args) {
        Pet dog = new Pet();
        dog.name = "旺财";
        dog.age = 3;
        dog.shout();

        System.out.println(dog.name);
        System.out.println(dog.age);

        Pet cat = new Pet();
    }
}

//宠物类
class Pet{
    String name;
    int age;

    //无参构造器

    public void shout(){
        System.out.println("宠物会叫");
    }
}
  • 为什么同时 new 的都是 Pet 但是会产生不一样的对象?
    • 因为是不同的区域,而它的地址不同,引用变量名不同

5.总结

  1. 类与对象
    • 类是一个抽象的模板,对象是一个具体的实例
  2. 方法
    • 定义、调用
    • 避免方法的死循环
    • 方法的定义格式要知道
  3. 对象的引用(引用就是指向对象的一个地址,用的是栈里面的一个引用,真实操作的是堆)
    • 引用类型(除了基本类型都是引用类型) < -- > 基本类型(8个)
    • 对象是通过引用来操作的:栈 --> 堆(真实对象在堆里面,地址)
  4. 属性:字段、 Field 、成员变量
    • 默认初始化
      • 数字:0 0.0
      • char :u0000
      • boolean : false
      • 引用类型: null
    • 定义
      • 修饰符 属性类型 属性名 = 值;
  5. 对象的创建和使用
    1. 必须使用 new 关键字创建对象,必须有构造器 Person zhangsan = new Person();
    2. 调用对象的属性 zhangsan.name
    3. 调用对象的方法 zhangsan.study()
  6. 类中只有两个东西:
    1. 静态的属性:属性
    2. 动态的行为:方法

四、面向对象三大特性(封装、继承、多态)

1.封装(外部不可以直接访问,通过内部方法访问调了一个接口)

  • 该露的露,该藏的藏:把该暴露给用户的一些东西暴露出来,但大部分程序的一些细节都应该藏起来

    • 我们程序设计要追求“高内聚,低耦合”。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
  • 封装(数据的隐藏)

    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
  • 属性私有,getXxx() / setXxx()

  • IDEA快捷键: alt + ins 快速生成 get / set 方法

  • 封装的意义:

    1. 提高程序的安全性,保护数据
    2. 隐藏代码的实现细节
    3. 统一接口
    4. 提高系统的可维护性
public class Encapsulation {
    public static void main(String[] args) {
        Student zhangsan = new Student();

        zhangsan.setName("张三");
        System.out.println(zhangsan.getName());  //张三
        zhangsan.setAge(120);  //不合法
        System.out.println(zhangsan.getAge());
    }
}

//学生类  private :私有,封装的核心
class Student {

    //封装大多数时候是对于属性来的,对于方法用的比较少
    //属性私有,属性私有的时候,一般使用 private
    private String name;  //名字
    private int id;  //学号
    private char sex;  //性别
    private int age;  //年龄

    //提供一些可以操作这个属性的方法( public 的 get / set 方法)
    // get :获得这个数据
    public String getName() {
        return this.name;
    }

    // set :给这个数据设置值
    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    //由于封装过了,可以做一些安全性检查
    public void setAge(int age) {
        if (0< age && age < 150) {
            this.age = age;
        } else {  //不合法
            this.age = 18;
        }
    }
}
  • 封装是对一个对象的封装,对一个类的封装

2.继承

  • 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
  • extends 的意思是“扩展”。子类是父类的扩展。
  • Java 中类只有单继承,没有多继承!
  • 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字 extends 来表示。
  • 子类和父类之间,从意义上讲应该具有“ is a ”的关系。
  • 子类可以继承父类的所有方法
  • object 类:在 Java 中,所有的类都默认直接 / 间接继承 Object 类
public class Inheritance_Inheritance {
    public static void main(String[] args) {

        Teacher teacher = new Teacher();
        teacher.say();  //说话
        System.out.println(teacher.getMoney());  //1000000000
    }
}

//人 :基类 / 父类
//在 Java 中,所有的类都默认直接 / 间接继承 Object 类
class Person /*extends Object*/ {

    //修饰符
    //public:公共的
    //protected:受保护的
    //default:默认的,可以不写
    //private:私有的,一般属性才是私有的
    private int money = 10_0000_0000;

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void say(){
        System.out.println("说话");
    }
}

//老师 is 人 :派生类 / 子类
//子类继承了父类,就会拥有父类的全部方法
class Teacher extends Person{}

//医生 is 人 :派生类 / 子类
class Doctor extends Person{}
  • super 注意点:

    1. super 调用了父类的无参构造器,必须要在子类构造器的第一行方法重写
    2. super 必须只能出现在子类的方法或构造方法中,在自己的类调用 super ,调用的是 Object
    3. super 和 this 不能同时调用构造方法,同时调用的话必须保证在第一行,两个肯定有一个报错
  • super 和 this 的区别:

  1. 代表的对象不同:
    • this :代表本身调用者这个对象
    • super :代表父类对象的引用
  2. 前提不一致:
    • this :没有继承也可以使用
    • super :只能在继承条件下才可以使用
  3. 构造方法有区别:
    • this() :调用的本类的构造器
    • super() :调用的父类的构造器
public class Inheritance_Super {
    public static void main(String[] args) {
        Dog dog = new Dog();  //Pet无参构造器  Dog无参构造器
        dog.testName("吉利");
        dog.testPrint();
    }
}

//宠物
class Pet {
    //假如父类里面没有无参构造器,直接写一个有参构造器,子类不仅调用不了父类的无参构造器,而且写不了无参构造器,
    //因为父类无法写,子类也没发写,代码在去检查的时候就通过不了
    public Pet() {
        System.out.println("Pet无参构造器");
    }

    protected String name = "姓名";

    public void print(){
        System.out.println("Pet");
    }

    //私有的东西无法被继承
    private void selfish(){}
}

//狗
class Dog extends Pet{
    public Dog() {
        super();  //隐藏代码,调用了父类的无参构造器,必须要在子类构造器的第一行,不写的话,默认调用父类的无参构造器
//        this();  //必须要在构造器的第一行,调用构造器的话,要么调用父类的,要么调用子类的,只能在代码的第一行,不能写到其他地方
        System.out.println("Dog无参构造器");
    }

    public Dog(String name) {
        this.name = name;
    }

    private String name = "旺财";

    @Override
    public void print(){
        System.out.println("Dog");
    }

    public void testName(String name){
        System.out.println(name);  //吉利,实参name
        System.out.println(this.name);  //旺财,子类的name
        System.out.println(super.name);  //姓名,父类的name
    }

    public void testPrint(){
        print();  //Dog,子类的print()
        this.print();  //Dog,子类的print()
        super.print();  //Pet,父类的print()
    }
}
  • 方法重写(需要有继承关系,子类重写父类的方法,属性是没有重写的) ==> 多态

    1. 方法名必须相同
    2. 参数列表必须相同
    3. 修饰符:子类范围可以扩大( public > protected > default > private )
    4. 抛出的异常的范围:可以被缩小,但不能扩大( ClassNotFoundException < Exception )
  • 重写,就是子类的方法(方法名、方法参数)和父类必须要一致,方法体不同

  • 为什么需要重写?

    1. 父类的功能,子类不一定需要,或者不一定满足(父类的东西太多了,子类只需要一点点,或者,父类的东西太少了,子类不满足,需要把一些新的方法进行重写)
    • 因为有些东西不能走老路,要自己学会摸索、探究,这就是重写带来的价值
public class Inheritance_Override {
    public static void main(String[] args) {
        //静态方法和非静态方法区别很大
        //静态方法:方法的调用只和左边定义的数据类型有关,静态方法在类一加载就出来了
        //非静态方法:重写
        //重写跟静态方法无关,只跟非静态方法有关
        /*
        b 是 A new 出来的对象,因此调用了 A 的方法
        有 static 时, b 调用了 B 类的方法,因为 b 是用 B 类定义的
        没有 static 时, b 调用了对象的方法,因为 b 是用 A 类 new 出来的对象
        静态方法是类的方法,非静态方法是对象的方法
         */
        A a = new A();
        a.printStatic();  //A==>printStatic()
        a.print();  //A==>printStatic()

        //父类的引用指向子类的对象
        B b = new A();  //向上转型,类型转换
        b.printStatic();  //B==>printStatic()
        b.print();  //A==>printStatic(),子类重写了父类的方法
    }
}

//重写都是方法的重写,和属性无关
class B{
    public static void printStatic(){
        System.out.println("B==>printStatic()");
    }

    public void print(){
        System.out.println("B==>print()");
    }
}

//继承
class A extends B{
    public static void printStatic(){
        System.out.println("A==>printStatic()");
    }

    //重写
    @Override  //注解:有功能的注释
    public void print(){
        System.out.println("A==>print()");
    }
}
  • IDEA快捷键: 重写
  • IDEA快捷键: F4 ( ctrl + h )打开继承树

3.多态(编译,看左边;运行,看右边

  • 可以实现动态编译:类型的最终状态只有在执行过程中才可以决定,使可扩展性变得更强

  • 即同一方法可以根据发送对象的不同而采用多种不同的行为方式

  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类 / 有关系的类)

  • 注意:

    1. 多态是方法的多态,属性没有多态性
    2. 父类和子类要有联系(如果类型不匹配,会报类型转换异常 ClassCastException
    3. 多态存在的必要条件:
      1. 要有继承关系
      2. 方法需要重写(方法如果不重写,调用的都是各自的方法,就没有任何区别了)
        • 有些方法不能被重写
          1. static 静态方法:属于类的,不属于实例
          2. final 常量方法:被 final 修饰的就无法修改了,在常量池里面
          3. private 私有方法:父类私有的
      3. 父类引用指向子类对象 Father father = new Son();
public class Polymorphism_Polymorphism {
    public static void main(String[] args) {
        //一个对象的实际类型是确定的, new 了什么,就是什么
//        new Animal();
//        new Cat();
        //可以指向的引用类型就不确定了
        //子类能调用的方法都是自己的或者继承父类的
        Cat cat = new Cat();
        //父类型,虽然可以指向子类,但是不能调用子类独有的方法
        Animal animal = new Cat();  //父类的引用指向子类的对象
        Object object = new Cat();  //父类的引用指向子类的对象

        //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
        //如果子父类都有的情况下,只要子类没有重写父类,那么就调用父类的,如果子类重写了父类,就调用子类的
        //子类重写了父类的方法,执行子类的方法
        animal.print();  //Cat==>print()
        cat.print();  //Cat==>print()
        ((Cat) animal).cat();  //Cat==>cat()
    }
}

//动物
class Animal{
    public void print(){
        System.out.println("Animal==>print()");
    }
}

//猫
class Cat extends Animal{
    @Override
    public void print() {
        System.out.println("Cat==>print()");
    }

    public void cat() {
        System.out.println("Cat==>cat()");
    }
}
  • instanceof 引用类型转换,判断一个对象是什么类型,可以判断两个对象之间是否存在父子关系
  • 编译,看左边;运行,看右边。
public class Polymorphism_Instanceof {
    public static void main(String[] args) {
        //总结:编译,看左边;运行,看右边。
        //Object > String
        //Object > Plants > Flower
        //Object > Plants > Tree
        Object object = new Tree();

        //一般可以先通过 instanceof 判断类型是否相似,然后再进行强制转换
        System.out.println(object instanceof Tree);  //true
        System.out.println(object instanceof Plants);  //true
        System.out.println(object instanceof Object);  //true
        System.out.println(object instanceof Flower);  //false
        System.out.println(object instanceof String);  //false
        System.out.println();

        //Plants > Flower
        //Plants > Tree
        Plants plants = new Tree();

        System.out.println(plants instanceof Tree);  //true
        System.out.println(plants instanceof Plants);  //true
        System.out.println(plants instanceof Object);  //true
        System.out.println(plants instanceof Flower);  //false
//        System.out.println(plants instanceof String);  //编译报错
        System.out.println();

        //Plants > Tree
        Tree tree = new Tree();

        System.out.println(tree instanceof Tree);  //true
        System.out.println(tree instanceof Plants);  //true
        System.out.println(tree instanceof Object);  //true
//        System.out.println(tree instanceof Flower);  //编译报错
//        System.out.println(tree instanceof String);  //编译报错
        System.out.println();

        //类型之间的转换:基本类型转换
        //高                 低
        Plants plants1 = new Tree();
        //强制类型转换
        //将高类型对象转换为低类型,就可以使用低类型的方法了
        Tree tree1 = (Tree) plants1;
        tree1.printTree();  //Tree==>printTree()
        ((Tree) plants1).printTree();  //Tree==>printTree()

        //自动类型转换
        //子类转换为父类,可能会丢失自己本来的一些方法
        Tree tree2 = new Tree();
        Plants plants2 = tree2;
        tree2.printPlants();  //Plants==>printPlants()
    }
}

//植物
class Plants{
    public void printPlants(){
        System.out.println("Plants==>printPlants()");
    }
}

//树
class Tree extends Plants{
    public void printTree(){
        System.out.println("Tree==>printTree()");
    }
}

//花
class Flower extends Plants{}

总结:

  1. 父类引用指向子类对象
  2. 把子类转换为父类,向上转型(自动类型转换);把父类转换为子类,向下转型(强制类型转换),会丢失一些方法
  3. 方便方法的调用,减少重复的代码

五、static、final

1.static

public class Static_Static {
    private static int age;  //静态变量,对于类而言,在内存中只有一个,能被类中的所有实例共享
    private int score;  //非静态变量

    //静态方法:可以调用静态方法里面的所有东西,不能调用普通方法的
    public static void printStatic(){
        System.out.println("printStatic()");
    }

    //非静态方法:可以调用静态方法里面的所有东西
    public  void print(){
        System.out.println("print()");
    }

    public static void main(String[] args) {
        A_Static_A_Static a_static_a = new A_Static_A_Static();
        System.out.println(a_static_a.age);
        System.out.println(a_static_a.score);
        System.out.println(A_Static_A_Static.age);  //静态变量推荐使用 类名.变量 调用
//        System.out.println(A_Static_A.score);  //非静态变量不能这样使用
        System.out.println();

        A_Static_A_Static.printStatic();
        printStatic();
//        print();  //非静态方法不能这样使用
        new A_Static_A_Static().print();
    }
}

2.静态代码块

//静态导入包,下面可以直接调用
import static java.lang.Math.random;

public class Static_StaticCodeBlock {
    static {
        //静态代码块:永久只执行一次,加载初始化的数据,方便初始化一些东西,类一加载直接执行,
        System.out.println("1. 静态代码块");
    }

    {
        //匿名代码块:赋初始值,不建议这么写,创建对象的时候自动创建,在构造器之前
        System.out.println("2. 匿名代码块");
    }

    public A_Static_B_StaticCodeBlock() {
        System.out.println("3. 构造方法");
    }

    public static void main(String[] args) {
        /*
        静态代码块
        匿名代码块
        构造方法
         */
        Static_BStaticCodeBlock static_staticCodeBlock = new Static_StaticCodeBlock();
        System.out.println();
        /*
        匿名代码块
        构造方法
         */
        Static_StaticCodeBlock static_staticCodeBlock1 = new Static_StaticCodeBlock();

//        System.out.println(Math.random());
        System.out.println(random());
    }
}

3.final(常量)

  • 通过 final 修饰的类,不能被继承,没有子类

六、抽象类和接口

1.抽象类(abstract,抽象的抽象:约束)

  • abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。

  • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

  • 抽象类,不能使用 new 关键字来创建对象,它是用来让子类继承的。

  • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。

  • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

  • 总结:

    1. 抽象类,不能 new 这个抽象类,只能靠子类实现它,就是一个约束
    2. 抽象类中可以写普通方法,抽象方法必须在抽象类中
  • 思考:

    1. 抽象类既然不能 new 对象,那么存在构造器吗?

      • 抽象类中一定有构造器,便于子类实例化时调用
    2. 抽象类存在的意义是什么?

      • 将共有属性抽象出来,节省代码开发,提高开发效率,提高可扩展性
public abstract class Abstract_Abstract {
    public static void main(String[] args) {
        //1. 抽象类,不能 new 这个抽象类,只能靠子类实现它,就是一个约束
//        new Action();
    }
}

//abstract:抽象类  extends:单继承,没办法继承多个类(接口可以多继承)
abstract class Action{
    //约束:有人帮我们实现
    //abstract:抽象方法,只有方法名,没有方法的实现
    public abstract void doSomething();

    //2. 抽象类中可以写普通方法,抽象方法必须在抽象类中
    public void print(){
        System.out.println("Action");
    }
}

//抽象类的所有方法,都必须要由继承它的子类实现,除非,子类也是抽象类,那就没必要实现了,让子子类实现
class A extends Action{
    @Override
    public void doSomething() {

    }
}

2.接口(面向接口编程)

  • 普通类:只有具体的实现
  • 抽象类:具体实现和规范(抽象方法)都有
  • 接口:只有规范(自己无法写方法,比抽象类还要抽象的抽象,专业的抽象,专业的约束,可以实现约束和实现分离
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是……则必须能……”的思想。如果你是汽车,则必须能跑。
  • 接口的本质是契约,就像法律一样,制定好后大家都遵守。
  • OO (面向对象)的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言( C++ 、 Java 、C# 等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
  • 声明类的关键字是 class ,声明接口的关键字是 interface
  • 总结(接口的作用):
    1. Java 的接口是一个约束
    2. 定义一些方法,让不同的人实现
    3. 接口中的所有定义的方法其实都是抽象的, public abstract 的
    4. 接口中定义的属性都是静态的常量, public static final 的
    5. 接口不能被实例化,接口中没有构造方法
    6. 可以通过 implement 关键字实现多个接口
    7. 实现类必须要重写接口中的方法
//抽象的思维
public class Abstract_Interface {
    public static void main(String[] args) {
    }
}

//类里面有方法的实现,接口只有方法的定义,通过 interface 来定义
//用户业务,interface:接口定义的关键字,接口都需要有实现类
interface UserService{
    //接口中定义的属性都是静态的常量, public static final 的,一般不这么做
//    public static final int AGE = 18;
    int AGE = 18;

    //接口中的所有定义的方法其实都是抽象的, public abstract 的
//    public abstract void run();
    void run();

    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}

//时间业务
interface TimeService{
    void timer();  //计时
}

//抽象类:extends
//一个类可以通过 implement 关键字实现接口
//实现了接口的类,就需要重写接口中的方法

//Java 虽然是单继承,但是可以利用接口实现多继承
class UserviceImpl implements UserService, TimeService{
    @Override
    public void run() {

    }

    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}

七、内部类

  • 内部类就是在一个类的内部再定义一个类,比如, A 类中定义一个 B 类,那么 B 类相对 A 类来说就称为内部类,而 A 相对 B 类来说就是外部类了。
  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类
public class InnerClass {
    public static void main(String[] args) {
        //外部类通过 new 关键字实现
        Outer outer = new Outer();
        //通过外部类来实例化内部类
        Outer.Inner inner =outer.new Inner();
//        Outer.Inner inner =new Outer().new Inner();
        inner.in();  //Inner-->in()
        inner.getId();  //18

        //4. 匿名内部类:没有名字初始化类,不用将实例保存到变量中
        new Outer().out();
        new Uservice() {
            @Override
            public void run() {

            }
        };
//        Uservice uservice = new Uservice() {
//            @Override
//            public void run() {
//
//            }
//        };
    }
}

class Outer{
    private int id = 18;

    public void out(){
        System.out.println("Outer-->out()");
    }

    //1. 成员内部类
    public class Inner{
        public void in(){
            System.out.println("Inner-->in()");
        }

        //内部类可以直接获得外部类的一些私有属性 / 私有方法
        public void getId(){
            System.out.println(id);
        }
    }

    //2. 静态内部类
    public static class InnerStatic{
        public void in(){
            System.out.println("Inner-->in()");
        }

        //静态内部类没办法访问非静态的属性
//        public void getId(){
//            System.out.println(id);
//        }
    }

    public void method(){
        //3. 局部内部类
        class Inner{
            public void in(){
                System.out.println("Inner-->in()");
            }
        }
    }
}

//这个不能是 public ,一个 Java 类中只能有一个 public class ,可以有多个 class
class Inner{}

interface Uservice{
    void run();
}

8.面向对象

上一篇:2021-7-25 Two Substrings


下一篇:【ECharts】SSH+JQueryAjax+Json+JSP将数据库中数据填充到ECharts中