• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

Javaの面向对象

开发技术 开发技术 2周前 (04-07) 6次浏览

面向对象编程 (OOP )

1.初识面向对象

面向过程 & 面向对象

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

什么是面向对象

  • 面向对象编程(Object-Oriented Programming,OOP)
    面向对象编程的本质:以类的方式组织代码,以对象的形式组织(封装)数据
  • 面向对象是 抽象 的
  • 三大特性:
    • 封装
    • 继承
    • 多态
  • 从认识论角度考虑的话 是先有对象后有类。对象 是具体的事物;类 是抽象的,是对对象的抽象
  • 从代码运行角度考虑的话 是先有类后有对象。类是对象的模板

2.方法回顾和加深

  • 方法的定义
    • 修饰符
    • 返回类型
    • break(可以跳出 switch,结束循环) 和 return 的区别 …
    • 方法名:注意命名规范,要做到 见名知意
    • 参数列表:(参数类型 参数名)… 可变参数
    • 异常抛出:(后面会学到)
  • 代码
package Java面向对象;

import java.io.IOException;

//↓这里的 方法回顾和加深 是一个类 (用class修饰的就是类)
public class 方法回顾和加深1_方法的定义 {

    //↓这里的 main 是一个方法 (main方法是用来启动当前这个程序的)

    public static void main(String[] args) {

    }

    /*↓这里的 public 是修饰符 (表示是公共的,所有人都可以访问的)
       这里的 String 是返回值的类型 (表示调用这个方法,将会返回一段字符串)
       这里的 sayHello 是给这个方法取的名字
       这里的 小括号()里的 是方法的参数 (这里没写参数)
       接下来 大括号{}里的 是方法体 (这里没写方法体)
       最后是 return 是返回值 (和返回值类型是对应的 这里返回的就只能是字符串)
       return 的作用:结束方法,返回一个结果 (可能返回的结果为空 亦或不返回结果) */

    public String sayHello(){

        return "Hello,World!";

    }

    /*接上,需要注意的是:返回值类型为 void (也就是不会返回值) 时,return 是不必要的
      因为这个方法将不会反回任何内容,要写也可以,只能是 return; (返回一个 寂寞) */

    public void hello(){

        return;

    }

    //↓这里的 a 和 b 是形式参数

    public int max(int a,int b){

        return a > b ? a : b;    //? : 是三元运算符,这里的意思是:a>b 时,输出a;否则,输出b

    }

    /*↓这里的 throws 是抛出的意思,throws IOException 就是抛出一个IO异常 (Exception 中:异常)
       (现在只做了解) */

    public void readFile(String file) throws IOException{

    }

}
  • 方法的调用:

    • 静态方法
    • 非静态方法
    • 形参和实参
    • 值传递和引用传递
  • 代码 (方法的调用、静态方法、非静态方法)

package Java面向对象;

public class 方法回顾和加深2_方法的调用 {

    public static void main(String[] args) {

        //1.静态方法  加了static就是静态方法

        //调用静态方法: 类名.方法名 就可以直接调用

        Student_测试.say1();

        //2.非静态方法  没加static的就是静态方法

        //调用非静态方法: 需要先实例化这个类 new

        new Student_测试().say2();

        //一般写为:

        //↓ 对象类型 对象名 = 对象值  (标准格式)
        Student_测试 student_测试 = new Student_测试();
        //然后再调用里面的方法
        student_测试.say2();

    }

    //两个都是非静态方法时 可以直接相互调用

    public void a(){
        b();
    }
    public void b(){
        a();
    }

    //两个都是静态方法时 也可以直接相互调用

    public void c(){
        d();
    }
    public void d(){
        c();
    }

    //e是静态方法,而f是非静态方法时:静态方法不能直接调用非静态方法

    public static void e(){
        // f();    无法调用
    }
    public void f(){
        e();    //可以调用
    }

    /*这是因为静态方法 static(e) 是和类一起加载的,所以它的时间片特别早,类存在的时候它就存在了
      而非静态方法 f 是跟对象有关的,对象创建(类实例化 new)之后才存在
      也就是 一个已经存在的 调用 一个还不存在的 ,所以会报错  */

}
package Java面向对象;

//创建名为 Student_测试 的类

public class Student_测试 {

    //名为 say1 的静态方法

    public static void say1(){

        System.out.println("学生说话了");
    }

    //名为 say2 的非静态方法

    public void say2(){

        System.out.println("学生说话了");
    }
    
}
  • 代码 (形参和实参)
package Java面向对象;

public class 方法回顾和加深3_形参和实参 {

    public static void main(String[] args) {

        int add = new 方法回顾和加深3_形参和实参().add(1, 2);
        /*这里的 1和2 是会传给方法里 a和b 的实际参数
          实际参数 和 形式参数 的类型要对应! */

        System.out.println(add);

    }

    public int add(int a,int b){    //这里的 a和b 就是形式参数,只是占位符

        return a + b;

    }

}
  • 代码 (值传递)
package Java面向对象;

public class 方法回顾和加深4_值传递 {

    //值传递

    public static void main(String[] args) {

        int a = 1;
        System.out.println(a);    //这里输出了 1

        方法回顾和加深4_值传递.change(a);    //这里也输出了 1
        System.out.println(a);
    }


    public static void change(int a){
        a = 10;
    }    //返回值为空


}
  • 代码 (引用传递)
package Java面向对象;

public class 方法回顾和加深5_引用传递 {

    //引用传递:传递的是对象,本质还是值传递

    public static void main(String[] args) {

        Student student = new Student();

        System.out.println(student.name);    //输出 null

        方法回顾和加深5_引用传递.change(student);

        System.out.println(student.name);    //输出 低密度脂蛋白

    }

    public static void change(Student student){
    //student 是一个对象:指向的 ---> Student student = new Student();这是一个具体的人,可以改变属性!
        student.name = "低密度脂蛋白";
    }

}

//定义了一个 Student 类,有一属性:name

class Student{
    String name;    //如果没有被赋值,String 的默认值为 null

}

3.对象的创建分析

类与对象的关系

  • 类 是一种抽象的数据类型,它是对每某一类事物整体描述/定义,但并不能代表某一个具体的事物
    • 动物、植物、手机、电脑……
    • Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
  • 对象是抽象概念的具体实例
    • 张三就是一个人的一个具体实例,张三家的旺财就是狗的一个具体事例
    • 能够体现出特点、展示出功能的是具体的实例,而不是一个抽象的概念
  • 可以将这些思想 转换为代码来实现

创建与初始化对象

  • 使用new关键字创建对象

  • 使用new关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象 进行默认的初始化,以及 对类中构造器的调用

  • 类中的构造器也称为构造方法,是在进行 创建对象 的时候必须要调用的

    并且构造器有以下两个特点:

    1. 必须和类的名字相同
    2. 必须没有返回类型,也不能写word
  • 构造器必须要掌握

  • 代码 (一个 抽象的 类)

package Java面向对象.对象的创建分析;

//这是一个 创建与初始化对象_1Student 类,它是抽象的

public class 创建与初始化对象_1Student {

    //一个类里只可能存在两个东西,属性和方法

    //1.属性: 字段  例如
    String name;    //没有赋值之前 默认是 null
    int age;    //没有赋值之前 默认是 0

    //2.方法
    public void study(){
        System.out.println(this.name + "正在学习");
        //↑ 这里的 this 代表当前这个类,this.name 就是这个类里的一个属性,可以赋值给它
    }

}
  • 代码 (一个 将抽象的类实例化的 类)
package Java面向对象.对象的创建分析;

//一个项目应该只存在一个 main 方法 (这个类是实例的)

public class 创建与初始化对象_1Application {

    public static void main(String[] args) {

        /*创建与初始化对象_1Student 类是抽象的,需要实例化
          类实例化后,会返回一个自己的对象
          雨木木 对象 就是一个 创建与初始化对象_1Student 类 的具体实例 */

        创建与初始化对象_1Student 雨木木 = new 创建与初始化对象_1Student();
        创建与初始化对象_1Student 脂蛋白 = new 创建与初始化对象_1Student();

        System.out.println(雨木木.name);    //这里还没给对象 雨木木 赋值 输出默认值 null
        System.out.println(雨木木.age);    //这里还没给对象 雨木木 赋值 输出默认值 0

        System.out.println("==============================");
        
        雨木木.name = "lin";    //给对象 雨木木 的 name属性 赋值 lin
        雨木木.age = 26;    //给对象 雨木木 的 age属性 赋值 26

        //分别输出 对象 雨木木 的name属性 和 age属性
        System.out.println(雨木木.name);    //↑ 已经赋值 输出确定的值 lin
        System.out.println(雨木木.age);    //↑ 已经赋值 输出确定的值 26

        //脂蛋白 也是同理

    }

}
  • 代码 (↓ 2段代码 都是有关于构造器的内容)
package Java面向对象.对象的创建分析;

public class 创建与初始化对象_2Person {

/*现在这个文件是java文件,编译之后会生成c++lass文件  java ---> class
反编译 class 文件 ↓

package Java面向对象.对象的创建分析;

public class 创建与初始化对象_2Person {
    public 创建与初始化对象_2Person() {
    }
}

会发现和 java源文件 有所不同,它默认的加了一个方法,这个方法没有返回值
  而且 这个方法的名字 和 类名相同。这就是一个构造器 (没有任何参数 叫 无参构造器)

- 结论:一个类即使什么都不写,它也会存在一个方法

- 构造器的作用:
  1.可以实例化一些初始值 (初始化值)
  2.使用 new 关键字时,必须要有构造器 (使用 new 关键字,本质是在调用构造器) */

    String name;
    int age;

    //↓ 显示的 1无参构造器 (默认加的构造器在 java文件里 是不显示的)

    public 创建与初始化对象_2Person(){

    }

    //↓ 2有参构造器
    public 创建与初始化对象_2Person(String name){

        this.name = name;
        //属性的 name = 输入的 name 的值

    }
    //↑ 一旦定义了 2有参构造,1无参构造 就必需显示定义 (空着也可以)

    //下 3有两个参数的构造器
    public 创建与初始化对象_2Person(String name,int age){

        this.name = name;
        this.age = age;

    }
    //构造器1 是 默认的构造器,后面的 2 3 是重载(构造器)
}
/*总结:构造器:1.要和类名相同、 2.没有返回值
       作用:1.new 本质是在调用构造方法、 2.初始化对象的值
       注意:定义了有参构造后,如果想使用无参构造,那么必须显示的定义一个无参构造

       拓展:1.用快捷键 alt + Insert 生成(无选择-无参)构造器、
            2.this.axxx = bxxx  前面.axxx是当前类的参数,后面bxxx是穿进来的参数

 */
package Java面向对象.对象的创建分析;

//一个项目应该只存在一个 main 方法 (这个类是实例的)

public class 创建与初始化对象_Application {

     public static void main(String[] args) {

         //↓ 使用 new 实例化一个对象
         创建与初始化对象_2Person ymm = new 创建与初始化对象_2Person();

         System.out.println(ymm.name);
         //↑ 因为()没有传参数,会调用无参构造器,然而无参构造器里也是空的,所以这里输出默认值 null

         //↓ 使用 new 实例化一个对象
         创建与初始化对象_2Person lin = new 创建与初始化对象_2Person("lin");

         System.out.println(lin.name);
         //↑ 因为()里传了参数,会调用有参构造器,经过有参构造器后,这里输出确定值 lin

         //会根据需要自动选择构造器(方法重载),例如 ↓ 传两个参数,就会调用有两个参数的构造器
         lin = new 创建与初始化对象_2Person("lin", 26);
         System.out.println(lin.name);    //lin
         System.out.println(lin.age);    //26

     }

}

创建对象の内存分析

  • 代码 (需要结合下面的思维导图来看)
package Java面向对象;

import Java面向对象.创建对象の内存分析.Pet;

public class Application {
    /*1.代码运行到这里,会在 方法区 里生成一块 Application区域,
        里面有一个main方法,main方法里有 常量池,常量池里有 汪汪、喵喵 两个常量

    ↓ main方法在栈里的最底下,当main方法执行时: */
    public static void main(String[] args) {

        Pet dog = new Pet();
        /*2.代码运行到这里 ↑ ,mew Pet 会在 方法区 里再生成一块Pet区域,它有一些属性:name age shout,没有常量
            这个时候还没赋值,name和age 都还是默认值 null和 0

            与此同时(Pet dog 时),栈里面会出现一个名为 dog 的引用变量名,而它真正的对象会产生在 堆 里
            一个叫 new pet()、内存地址为0x0001 的区域里,目前里面所有的值也是默认的,
            而里面的方法 shout 是直接调用了 方法区Pet 里的 shout  */

        dog.name = "汪汪";
        dog.age = 3;
        /*3.代码运行到这里 ↑ ,把方法区 常量池里的 "汪汪" 赋值给个 栈 里 dog 所指向的 new pet() 里的 name 、
            后一句代码同理,把 3 赋值给同区域里的 age、而下面的 dog.shout() 就= 方法区Pet 里的 shout() 不用传递参数
            代码运行到现在,有关于 dog 的值也就都赋完了,下面是输出语句  */

        System.out.println("ta名字叫" + dog.name);
        System.out.println("今年" + dog.age + "岁了");
        dog.shout();

        System.out.println("==============================");

        Pet cat = new Pet();
        /*4.同理,此时 栈里面会出现一个名为 cat 的引用变量名,而它真正的对象会产生在 堆 里
            一个叫 new pet()、内存地址为0x0002 的区域里(和dog指向的区域不同),目前里面所有的值也是默认的,
            而里面的方法 shout 是直接调用了 方法区Pet 里的 shout  */

        cat.name = "喵喵";
        cat.age = 2;
        /*5.代码运行到这里 ↑ ,把方法区 常量池里的 "喵喵" 赋值给个 栈 里 cat 所指向的 new pet() 里的 name 、
            后一句代码同理,把 2 赋值给同区域里的 age、而下面的 cat.shout() 就= 方法区Pet 里的 shout() 不用传递参数
            代码运行到现在,有关于 cat 的值也就都赋完了,下面是输出语句  */

        System.out.println("ta名字叫" + cat.name);
        System.out.println("今年" + cat.age + "岁了");
        cat.shout();

        /*最后,方法区里还有一块区域:静态方法区,所有带 static 的关键词 都存放在里面
          它和类一起加载,所以无论那个对象,都可以直接的去调用它  */

    }

}
package Java面向对象.创建对象の内存分析;

public class Pet {

    public String name;
    public int age;

    //→ 这个位置其实是有一个 无参构造 (没有显示出来)

    public void shout(){
        System.out.println("ta叫了一声!");
    }

}

![思维导图](E:Java 笔记相关资料捕获3.PNG)

类与对象の小结

  1. 类与对象:

    类是一个模版,类是抽象的;对象是一个具体的实例,对象是具体的

  2. 方法:

    回顾方法的定义以及方法的调用

  3. 对象的引用:

    基本类型(8个);引用类型 (除了基本类型都是引用类型)

    对象是通过引用来操作的: 栈 —> 堆 (引用就是 指向对象的一个地址)

  4. 对象的属性: 属性 一般称之为 字段(Field), 也称 成员变量

    属性 默认会初始化: 初始化默认值为:

    数字:0 / 0.0 ; char:u0000 ; boolean:false ; 引用类型:null

    如何定义一个属性:修饰符 属性类型 属性名 = 属性值

  5. 对象的创建和使用:

    必须使用 new关键字 创造对象,想要创造对象必须得有构造器。例:Person ymm = new Person ( );

    对象的属性 ymm.name

    对象的方法 ymm.sleep ( )

  6. 类: 类里只有两个东西

    一是 属性 (静态的属性)

    二是 方法 (动态的方法)

4.面向对象三大特性

1封装

  • 该露的露,该藏的藏
    • 我们设计程序要追求 “高内聚,低耦合” 。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
  • 记住这句话就够了,属性私有,get / set
package Java面向对象.面向对象三大特征;

public class Application {

    public static void main(String[] args) {

        封装_Student s1 = new 封装_Student();

        s1.setName("雨木木");

        System.out.println(s1.getName());

        s1.setAge(-1);
        //↑ 年龄为-1 是不合法的,我们可以利用set方法,做一些安全性检查,规避掉不合法数据
        System.out.println(s1.getAge());

    }
}
package Java面向对象.面向对象三大特征;

//学生类
public class 封装_Student {

    //属性私有:private

    private String name;    //名字
    //↑ private和public 相对应,private 是私有的、public是公开的

    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;

    }

    /*总结:1.希望属性私有的时候,一般都是用 private 这个关键字
           2.getName setName 的name首字母都需要大写 (驼峰命名规则)

      拓展:快捷键 alt + insert 可以自动生成 get/set方法  */

    public int getAge() {
        return age;
    }

    //↓ 利用set方法,做一些安全性检查,规避掉不合法数据
    public void setAge(int age) {
        if(age > 0 && age < 130) {
            this.age = age;    //输入合法数据时,年龄 age = 输入的年龄
        }else{
            this.age = 0;    //输入不合法数据时,年龄 age = 0
        }
    }

    /*封装的意义:1.提高程序的安全性,保护数据 (set方法里可以做一些安全性检查)
                 2.隐藏代码的实现细节 (避免程序直接访问,只能通过操作接口操作)
                 3.统一接口 get/set
                 4.提高系统的可维护性 (良好的封装便于我们修改其内部代码)  */
}
/*回顾:在Java中,判断一个类里的两个方法是否相同,主要参考两个方面:方法名和参数列表
       若两者均相同,那么 它们就是同一个方法  */

2继承

  • 继承的本质是对某一批类的抽象。从而实现对现实世界更好的建模

  • extends 的意思是“扩展”,子类是父类的扩展

  • Java中类 只有单继承,没有多继承(类似 一个儿子只能有一个父亲,但一个父亲可以有多个儿子)

  • 继承是类和类之间的一种关系。除此之外类和类之间的关系 还有 依赖、组合、聚合…

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类 使用关键词 extends 来表示

  • 子类和父类之间的关系,从意义上讲应该具有 “xx is a XX” 的关系(Student is a Person)

  • 代码(Application 测试)

package Java面向对象.面向对象三大特征2_继承;

public class Application {

    public static void main(String[] args) {

        Student student = new Student();

        student.say();
        /*↑ 因为学生类 继承了 人类 的say方法,所以这里也可以直接调用
            子类继承了父类,就会拥有父类的全部方法(私有除外)  */

        System.out.println(student.money);
        /*↑ 因为子类-学生类 继承了 父类-人类 的money属性,所以这里也可以直接调用
            子类继承了父类,就会拥有父类的全部属性(私有除外)  */

        //System.out.println(student.height);    X
        /*↑ 因为子类-学生类 不能继承了 父类-人类 的私有属性 height,所以这里无法调用
            子类继承父类,不能继承父类的私有属性  */

    }

}
  • 代码(Person 父类)
package Java面向对象.面向对象三大特征2_继承;

//Person 人类  (基类/父类)
public class Person {

    //↓ 下面两个带有 public 的方法和属性,都可以被子类直接继承

    public void say(){
        System.out.println("说了一句话~");
    }

    public int money = 10_0000_0000;

    //↓ 下面这个带有 private(私有) 的属性,不能被子类继承

    private int height = 180;

    /*总结一下,前面的修饰符有四种,包括:
    public(公开的)、protected(受保护的)、default(默认的,也就是什么都不写)、 private(私有的)
    其中,只有private(私有的)不能被子类继承。但如果希望子类继承,一般用 public(公开的)  */

    //虽然不能直接继承 private(私有的),但是也可以通过 get/set 来进行操作

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    /*拓展:通过 快捷键 Ctrl + H ,可以打开 类层次结构(继承树),通过这个操作还能发现:
           在Java中,所有的类,都默认(直接/间接)继承Object类  */

}
  • 代码(Student 子类)
package Java面向对象.面向对象三大特征2_继承;

//Student 学生类  (派生类/子类),学生也是人,Student is a Person
public class Student extends Person {

}

Super 详解

  • 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征3_Super详解;
//测试

import Java面向对象.面向对象三大特征3_Super详解.Person;
import Java面向对象.面向对象三大特征3_Super详解.Student;

public class Application {

    public static void main(String[] args) {

        Student student = new Student();

        student.test("YMM");

        System.out.println("==============================");

        student.test1();

        System.out.println("==============================");

        new Student();
        /*↑ 这里输出了Person 类の无参执行了
                     Student 类の无参执行了
            说明:在执行子类Student的无参构造之前,默认先调用了父类Person的无参构造
            这其实是:子类里隐藏了一段 调用父类无参构造器 的代码  */

    }

}
package Java面向对象.面向对象三大特征3_Super详解;
//父类
public class Person {

    //↓ 把无参构造器显示出来
    public Person(){
        System.out.println("Person 类の无参执行了");
    }

    protected String name = "LDL";
    //↑ 现在在父类里面就有了一个属性

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

}
package Java面向对象.面向对象三大特征3_Super详解;
//子类
public class Student extends Person{

    //↓ 把无参构造器显示出来
    public Student(){
      /*!这里有一段隐藏代码:super(); 调用了父类的无参构造:
         而且如果把 super(); 显示的定义出来了,必须在子类无参构造器的第一行
         可以理解为:在执行子类无参之前,必须调用父类的无参构造,这是默认的,而且必须最先执行(最高优先级)

         这就引出了另一个问题:如果父类的第一个构造器是有参构造器 (也就是当父类没了无参构造器)
         子类就:不仅无法调用父类的无参构造,而且子类也无法写无参构造,只能写有参/调用父类的有参构造  */

        System.out.println("Student 类の无参执行了");
    }

    private String name = "低密度脂蛋白";

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

    public void test(String name){
        System.out.println(name);    //传递进来的的那个参数 YMM
        System.out.println(this.name);    //这个类里的属性 低密度脂蛋白
        System.out.println(super.name);    //父类的属性 LDL
    }

    public void test1(){
        print();    //调用的是当前这个类的 print 方法
        this.print();    //意思和 ↑ 一样
        super.print();    //调用的是 父类 里的 print 方法
    }

}
/*总结:
  superの注意点:
    1.super. 是调用父类的构造方法。而且 super();(调用父类无参)必须在子类无参构造器的第一行
    2.super. 必须只能出现在 子类的方法/构造方法中
    3.super. 和 this. 不能同时调用构造方法(因为都必须在最前面,同时调用总会有一个报错)

  Vs this :
    1.代表的对象不同: this:本身调用者这个对象(当前对象)
                     super:代表对 当前对象的父类对象 的引用
    2.使用的前提不同: this:没有继承的情况下也可以使用
                     super:只能在继承条件下才能使用
    3.构造方法的区别: this();本类的构造
                     super();父类的构造  */

方法の重写

  • 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征4_方法の重写;

public class Application {

    public static void main(String[] args) {

        A a = new A();
        a.test();    //这里走的是A类的方法

        //↓ A与B是继承的关系,这里父类的引用指向了子类
        B b = new A();
        b.test();    //这里走的是B类的方法

        //可以得出:方法的调用只和左边的类型(定义的数据类型)有关

    /*!上面的结果是 A B 里的方法都是静态方法(带有static-被注掉的部分)的情况下得到的
      而现在(方法都为非静态方法的情况下)再运行时,两个走的都是A类的方法,由此可以得出:
      1.静态的方法和非静态的方法区别很大
      2.在非静态方法时:B b = new A();    //← 这里子类重写了父类的方法
                      b.test();
                      //↑ 这里虽然走的也是B类的方法,但是B类的方法被子类A类重写了  */
    }

}
package Java面向对象.面向对象三大特征4_方法の重写;
//父类
public class B {

    //重写都是方法的重写,和属性无关,所以这里直接写方法作为演示

    /*public static void test(){
        System.out.println("B==>test()");

    }  */

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

    }

}
package Java面向对象.面向对象三大特征4_方法の重写;
//子类
public class A extends B {

    /*public static void test(){
        System.out.println("A==>test()");

    }  */

    //↓ 下面这个方法是用快捷键生成的
    @Override    //注解:有功能的注释;单词的意思:重写
    public void test() {
        System.out.println("A==>test()");
    }
}

/*重写の总结:
  重写 需要满足的条件:
    1.需要有继承关系
    2.只能是子类重写父类的方法 (不能去重写父类的属性)
    3.方法名必须相同
    4.参数列表也必须相同  3+4也就是说:重写 子类的方法和父类的方法必须要一致,方法体不同 即可
    5.修饰符:范围可以扩大 但不能缩小:public(公开的)>protected(受保护的)>default(默认的,也就是什么都不写)> private(私有的)
    6.抛出的异常:范围可以被缩小 但不能扩大:Exception(大)--->ClassNotFoundException(小)
  为什么需要重写:
    1.父类的功能,子类不一定需要
    2.父类的功能,不一定能满足子类的需求
  重写的快捷键:
    ALT + INS -> 重写方法  */

3多态

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

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

  • 多态存在的条件

    • 有继承关系
    • 子类重写父类的方法
    • 父类引用指向子类对象
  • 注意,多态是方法的多态,属性没有多态性

  • 多态的存在非常重要。存在的意义:可以使程序实现动态编译,也就是程序的最终状态在执行时才被确定,这便大大的增强了程序的可扩展性

  • 代码

package Java面向对象.面向对象三大特征5_多态;

public class Application {

    public static void main(String[] args) {

        /*一个对象的实际类型是确定的,例如:
        new Student();
        new Person();
        new一个什么,它就是什么类型的对象

        但是它可以指向的引用类型就不确定了  */

        //Student(子) 能调用的方法都是自己的或者继承父类的
        Student s1 = new Student();
        //Person(父) 可以指向子类,但是不能调用子类独有的方法
        Person s2 = new Student();    //父类的引用指向子类

        //对象能执行哪些方法,主要看=左边的类型,和=右边关系不大
        s2.run();    //子类重写了父类的方法,执行的还是子类的方法
        s1.run();

        //s2.eat();← 这里不能调用,因为 父类型Person 不能调用子类独有的方法

        ((Student) s2).eat();
        //↑ 敲s2.eat();时,IDEA可以帮助强制转换(高->低),这里转换后可以执行
        s1.eat();

    }

}
package Java面向对象.面向对象三大特征5_多态;
//父类
public class Person {

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

}
package Java面向对象.面向对象三大特征5_多态;
//子类
public class Student extends Person {

    @Override
    public void run() {
        System.out.println("son");
    }

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

/*关于 多态 的注意事项:
  1.多态是方法的多态,属性没有多态
  2.父类和子类(有联系),才可以多态。XJB转换的话会报错:ClassCastException(类型转换异常)
  3.多态存在的条件:继承关系、方法需要重写、父类引用指向子类对象:father f1 = new Son();
    关于方法需要重写:1.static(静态)的方法是属于类的,它不属于实例,不能重写
                    2.final(常量)的方法在常量池里,不能重写
                    3.private(私有)的方法,也不能重写
                    ↑.这三种方法没办法重写,更不可能实现多态了  */

instanc++eof 和 类型转换

  • 代码(都在代码里了 :)
package Java面向对象.面向对象三大特征6_instanceof和类型转换;

public class Application {

    public static void main(String[] args) {

        //↓有关于instanceof的一些操作:

        //Object > String
        //Object > Person > Teacher
        //Object > Person > Student

        Object object = new Student();
        System.out.println(object instanceof Object);    //ture
        System.out.println(object instanceof String);    //false
        System.out.println(object instanceof Person);    //ture
        System.out.println(object instanceof Teacher);    //false
        System.out.println(object instanceof Student);    //ture

        System.out.println("==============================");

        Person person = new Student();
        System.out.println(person instanceof Object);    //ture
        //System.out.println(person instanceof String);    编译报错
        System.out.println(person instanceof Person);    //ture
        System.out.println(person instanceof Teacher);    //false
        System.out.println(person instanceof Student);    //ture

        System.out.println("==============================");

        Student student = new Student();
        System.out.println(student instanceof Object);    //ture
        //System.out.println(student instanceof String);    编译报错
        System.out.println(student instanceof Person);    //ture
        //System.out.println(student instanceof Teacher);    编译报错
        System.out.println(student instanceof Student);    //ture


        /*总结:System.out.println(X instanceof Y);能不能编译通过,取决于X和Y之间知否存在父子关系
               结果为ture还是false,主要是看变量X所指向的实际类型是不是Y的子类型  */

        System.out.println("==============================");

        /*↓有关于类型之间的转换:类似于基本类型转换,64 32 16 8 从低转到高自动转换,从高转到低需要强制转换
          这里可以理解为:父类代表高的,子类代表低的  */

        //这里Person是高的,Student是低的,可以直接就转换了
        Person student1 = new Student();


        /*student1.go();←这里报错了,是因为student1对象现在是Person类型的,而Person类型下没有go方法
          ↓我们只需要将对象student1的类型强转(Person高 转 Student低)为Student类型,就可以使用Student里面的go方法了  */

        ((Student)student1).go();

        Student student2 = new Student();
        student2.go();
        Person person2 = student2;
        //person1.go();←这里报错了,是因为子类转换为父类,有可能丢失自己本来的一些方法


    }

}

/*小结:
  1.父类引用指向子类对象
  2.把子类转换为父类,向上转型,不用强制转换
  3.把父类转换为子类,向下转型,需要强制转换(有可能丢失一些方法)
  4.意义:方便方法的调用,减少重复的代码,使代码更简洁  */
package Java面向对象.面向对象三大特征6_instanceof和类型转换;

public class Person {

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

}

package Java面向对象.面向对象三大特征6_instanceof和类型转换;

public class Student extends Person {

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

}

package Java面向对象.面向对象三大特征6_instanceof和类型转换;

public class Teacher extends Person {

}

static++ 关键字详解

  • 代码(都在代码里了 :)
package Java面向对象.static关键字详解;

//static
public class Student {

    public static int age;    //静态的变量
    private double score;    //非静态的变量

    public void run(){

    }
    public static void go(){

    }


    public static void main(String[] args) {

        Student s1 = new Student();

        System.out.println(Student.age);
        //System.out.println(Student.score);←报错,因为score是非静态的变量,类不能直接调用

        System.out.println(s1.age);
        System.out.println(s1.score);

        //run();←直接调用run方法会报错,因为它不是静态方法,需要先new出Student,再调用它的run方法↓
        new Student().run();    //对象.方法

        go();    //而静态的go方法可以直接使用

        /*且在非静态方法中可以直接调用静态方法,因为static静态方法是同类一起加载的,很早就在那儿了,直接用就行
          但是在静态方法中不能直接调用非静态方法,因为非静态方法这时候还不存在,new之后才存在,所以不能直接调用
         */

    }

}
package Java面向对象.static关键字详解;

//tips:↓被final修饰的类不能被继承,也就是不能有子类了(断子绝孙)
public final class Person {

    /*{
        //代码块(匿名代码块),程序在执行的时候并不能主动调用这些模块
        //可以用来赋初始值
    }

    static {
        //静态代码块,类一旦加载就开始执行,且永久只执行一次
    }*/

    /*案例:person1执行时,将按照123的循序依次执行
           person2执行时,将按照23的循序依次执行,不会再执行静态方法
     */

    //2
    {
        System.out.println("匿名代码块");
    }

    //1  只会在第一次执行一次
    static {
        System.out.println("静态代码块");
    }

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

    public static void main(String[] args) {

        Person person1 = new Person();

        System.out.println("==============================");

        Person person2 = new Person();

    }

}
package Java面向对象.static关键字详解;
//静态导入包*
import static java.lang.Math.random;
import static java.lang.Math.PI;

public class Test {

    public static void main(String[] args) {

        System.out.println(Math.random());    //输出一个随机数

        //↓在静态导入包*后,可以不用再写Math.,直接用random()方法就行
        System.out.println(random());
        //↓常量PI也同理
        System.out.println(PI);

    }

}

5.抽象类和接口

1抽象类

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

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

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

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

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

  • 代码

package Java面向对象.抽象类;

//↓一个类加上abstract关键字,这个类就成为了抽象类
public abstract class Action {

    //约束,将有人帮我们实现
    //↓加上abstract,成为抽象方法,只有方法名,没有方法的实现(没有方法体)
    public abstract void doSomething();

    /*小结:
      1.不能new这个抽象类,只能靠子类去实现它;它只是一个约束
      2.抽象类中可以写普通方法
      3.抽象方法必须在抽象类中

      抽象的抽象:只是一个约束  */

}

package Java面向对象.抽象类;

//抽象类的所有方法,继承了它的子类,都必须要实现它的抽象方法
public class A extends Action {
    @Override
    public void doSomething() {
        
    }
}

2接口

  • 普通类:只有具体实现

  • 抽象类:具体实现和规范(抽象方法)都有

  • 接口:只有规范,自己无法写方法,它是专业的约束。达到 约束和实现 分离的效果,从而实现 面向接口编程

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想,如:如果你是飞机,则必须能飞

  • 接口的本质是契约。就像我们人的法律一样,制定好后大家都遵守

  • 面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象

  • 声明类的关键字是class;声明接口的关键字是interface

  • 代码

package Java面向对象.接口;

//↓用关键字 interface 来声明这是一个接口
public interface UserService {

    /*接口中的所有定义的方法都是抽象的,不写默认前面都有 public abstract
      void run(); = public abstract void run();

      接口中的所有定义的属性都是常量,不写默认前面都有 public static final
      int AGE = 99; = public static final int AGE = 99;
      但 我们一般不会在接口里定义常量,只在接口里定义方法以作为约束  */

    void add(String name);
    void delete(String name);
    void updata(String name);
    void query(String name);

}
package Java面向对象.接口;

public interface TimeService {

    void timer();

}
package Java面向对象.接口;

//类 可以通过 implements关键字 来实现接口;实现了接口的类,就需要重写接口中的方法

//↓利用接口实现了多继承
public class UserServiceImpl implements UserService,TimeService{
    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void updata(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}
  • 接口的小结:
    • 接口可以 起到约束的作用
    • 接口可以 定义一些方法,让不同的人实现
    • 接口中的 方法都是public abstract;接口中的 常量都是public static final
    • 接口中没有构造方法,因此也不能被实例化(new)
    • 类中使用关键字implements可以实现多个接口(利用接口实现多继承)
    • 若一个类要实现接口,就必须要重写对应接口中的所有方法

6.内部类

1内部类

  • 内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类

  • 内部类可以分为:

    • 成员内部类
    • 静态内部类
    • 局部内部类
    • 匿名内部类
  • 代码

package Java面向对象.N种内部类;

public class Outer {

    private int id = 10;

    public void out(){
        System.out.println("外部类的方法");
    }

    //↓局部内部类(在方法里面的类)
    public void method(){
        class Inner{
        }
    }

    //↓成员内部类(在类里面的类) 加static修饰就是 静态内部类
    public class Inner {

        public void in(){
            System.out.println("内部类的方法");
        }

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

//一个java类中,可以有多个class类,但只能有一个public class
class A {
}
package Java面向对象.N种内部类;

public class Application {

    public static void main(String[] args) {

        //↓通过new关键字 实例化外部类
        Outer outer = new Outer();

        //通过外部类来实例化内部类
        Outer.Inner inner = outer.new Inner();
        inner.in();
        inner.getId();


    }

}
package Java面向对象.N种内部类;

public class Test {

    public static void main(String[] args) {

        //↓正常的 有名字初始化类
        Apple apple = new Apple();

        //↓没有名字初始化类,不用将实例保存到变量中
        new Apple().eat();

        UserService userService = new UserService() {

            @Override
            public void hello() {

            }
        };
    }
}

class Apple {

    public void eat(){
        System.out.println("1");
    }
}

interface UserService{

    void hello();

}
  • ↑ 关于这些沆瀣一气的内部类,现在只做了解即可,后面遇了到再深入学习

end .


程序员灯塔
转载请注明原文链接:Javaの面向对象
喜欢 (0)