Java学习笔记(七)——面向对象编程(中级)

一、IDEA

(一)常用的快捷键

(二)模版/自定义模版

二、包

(一)包的命名

(二)常用的包

(三)如何引入(导入)包

(四)注意事项和使用细节

三、访问修饰符

四、OOP三大特征

(一)封装

(二)继承

继承的细节问题

继承的本质

(三)多态

多态的具体体现

多态的注意事项和细节1

1、多态的向上转型

2、多态的向下转型

多态的注意事项和细节2

java的动态绑定机制(重要)

多态的应用

五、Super关键字

super和this的比较

六、方法重写/覆盖(overwrite)

方法重写和方法重载的比较

七、Object类详解

(一)equals方法

(二)hashCode方法

(三)toString方法

(三)finalize方法

八、断点调试(debug)

一、IDEA

(一)常用的快捷键

自己配置:

1、删除当前行,ctrl + d

2、复制当前行,ctrl + alt + 向下光标

3、补全代码 alt + /

4、添加注释和取消注释 ctrl + /

5、导入该行需要的类,先配置auto import,然后使用alt + enter即可

6、快速格式化代码 ctrl + alt + L

7、快速运行程序 alt + R

8、生成构造方法等 alt + insert (提高开发效率)

9、查看一个类的层级关系 ctrl + H (学习继承后,非常有用)

10、将光标放在一个方法上,ctrl + B,可以选择定位到哪个类的方法 (学习继承后,非常有用)

11、自动的分配变量名,通过在后面.var

(二)模版/自定义模版

二、包

包的三大作用:

1、区分相同名字的类

2、当类很多时,可以很好的管理类(看Java API文档)

3、控制访问范围

包基本语法:

package com.hspedu;

包的本质分析:实际上就是创建不同的文件夹来保存类文件

包的快速入门:

(一)包的命名

1、命名规则:

只能包含数字、字母、下划线、小圆点,但不能使用数字开头,不能是关键字或保留字

demo.class.exec1 (×,class是关键字)

demo.12a (×,12a是数字开头的)

demo.ab12.oa (√)

2、命名规范:

一般是小写字母+小圆点,一般是:

com.公司名.项目名.业务模块名

比如,com.hspedu.oa.model; com.hspedu.oa.controller;

举例,com.sina.crm.user //用户模块

com.sina.crm.order //订单模块

com.sina.crm.utils //工具类

(二)常用的包

一个包下,包含很多的类,java中常用的包有:

java.lang.* //lang包是基本包,默认引入,不需要再引入,比如使用Math.abs()

java.util.* //util包,系统提供的工具包,工具类,比如使用Scanner

java.net.* //网络包,网络开发

java.awt.* //是做java的界面开发,GUI

(三)如何引入(导入)包

语法: import 包;

引入一个包的主要目的是要使用该包下的类,比如import java.util.Scanner;就只是引入一个类Scanner。(import java.util.*表示将java.util包所有都引入,但建议需要使用什么类就导入什么类)

java 复制代码
package com.hspedu.pkg;

import java.util.Arrays;
import java.util.Scanner;
//import java.util.*;
public class Import01 {

    public static void main(String[] args) {
        int arr[] = {-1, 20, 2, 13, 3};
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
    }
}

(四)注意事项和使用细节

1、package的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package。

2、import指令位置放在package的下面,在类的定义前面,可以有多句且没有顺序要求。

三、访问修饰符

java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

1、公开级别:用public修饰,对外公开

2、受保护级别:用protected修饰,对子类和同一个包中的类公开

3、默认级别:没有修饰符号,向同一个包的类公开

4、私有级别:用private修饰,只有类本身可以访问,不对外公开

使用的注意事项:

1、修饰符可以用来修饰类中的属性,成员方法以及类

2、只有默认的和public才能修饰类,并且遵循上述访问权限的特点

四、OOP三大特征

面向对象编程有三大特征:封装、继承和多态

(一)封装

封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。

封装的理解和好处:

1、隐藏实现细节 方法<---调用(传入参数)

2、可以对数据进行验证,保证安全合理

封装的实现步骤:

1、将属性进行私有化(不能直接修改属性)

2、提供一个公共的set方法,用于对属性判断并赋值

public void setXxx(类型 参数名) {

// 加入数据验证的业务逻辑

属性 = 参数名;

}

3、提供一个公共的get方法,用于获取属性的值

public XX getXxx() { //权限判断

return xx;

}

快速入门:案例-不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认。年龄必须在1-120,name的长度在2-6字符。

java 复制代码
package com.hspedu.encap;

public class Encapsulation01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("jack");
        person.setAge(20);
        person.setSalary(30000);
        System.out.println(person.info());

        //如果使用构造器指定属性
        Person smith = new Person("smith", 80, 50000);
        System.out.println("======smith信息=====");
        System.out.println(smith.info());
    }
}
class Person {
    public String name;
    private int age;
    private double salary;

    public Person() {
    }

    public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;
        // 可以将set方法写在构造器中,这样仍然可以验证
        this.setName(name);
        this.setAge(age);
        this.setSalary(salary);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 加入对数据的校验
        if(name.length() >=2 && name.length() <= 6){
            this.name = name;
        } else {
            System.out.println("名字长度不对,需要(2-6)个字符,给默认名字");
            this.name = "无名";
        }

    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // 加入对数据的校验
        if(age >= 1 && age <= 120){
            this.age = age;
        } else {
            System.out.println("你设置的年龄不对,需要在(1-120),给默认年龄为18");
            this.age = 18;
        }

    }

    public double getSalary() {
        // 这里可以增加对当前对象的权限判断
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    public String info() {
        return "信息为 name=" + name + " age=" + age + " salary=" + salary;
    }
}

(二)继承

编写了两个类,一个是Pupil类(小学生),一个是Graduate(大学毕业)。问题:两个类的属性和方法很多是相同的,怎么办?继承(代码复用性)

介绍:继承可以解决代码复用,让编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

基本语法:

class 子类 extends 父类 {

}

1、子类就会自动拥有父类定义的属性和方法

2、父类又叫超类、基类

3、子类又叫派生类

快速入门:

java 复制代码
package com.hspedu.extend_;

public class Student {
    public String name;
    public int age;
    private double score;

    public void setScore(double score) {
        this.score = score;
    }
    public void showInfo() {
        System.out.println("学生名=" + name + " 年龄=" + age + " 成绩=" + score);
    }
}
java 复制代码
package com.hspedu.extend_;

public class Pupil extends Student{

    public void testing() {
        System.out.println("小学生" + name + "正在考小学数学...");
    }
}
java 复制代码
package com.hspedu.extend_;

public class Graduate extends Student{

    public void testing() {
        System.out.println("大学生" + name + "正在考大学数学...");
    }
}
java 复制代码
package com.hspedu.extend_;

public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "小红";
        pupil.age = 10;
        pupil.testing();
        pupil.setScore(60);
        pupil.showInfo();

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

        Graduate graduate = new Graduate();
        graduate.name = "大红";
        graduate.age = 20;
        graduate.testing();
        graduate.setScore(100);
        graduate.showInfo();

    }
}

继承给编程带来了便利:

1、代码复用性提高了

2、代码的扩展性和可维护性提高了

继承的细节问题

1、子类继承了所有的属性和方法,非私有的属性和方法可以直接在子类直接访问;但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。

2、子类必须调用父类的构造器,完成父类的初始化。

3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。

4、如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)。

5、super在使用时,必须放在构造器第一行。

6、super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器中。

7、java所有类都是Object类的子类(Object是所有类的基类)。

8、父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)。

9、子类最多只能继承一个父类(指直接继承),即java中是单继承机制。

10、不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。

继承的本质

分析当子类继承父类,创建子类对象时,内存中到底发生了什么?当子类对象创建好后,建立查找的关系

练习:

(三)多态

传统方法解决(代码复用性不高,而且不利于代码维护,引出多态):

多态的基本介绍:方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

1、方法的多态:重载和重写就体现多态

2、对象的多态(核心):

(1)一个对象的编译类型和运行类型可以不一致

(2)编译类型在定义对象时,就确定了,不能改变

(3)运行类型是可以变化的

(4)编译类型看定义时=号的左边,运行类型看=号的右边

Animal animal = new Dog(); animal编译类型是Animal,运行类型是Dog

animal = new Cat(); animal的运行类型变成了Cat,编译类型仍然是Animal

快速入门:

java 复制代码
package com.hspedu.poly_;

public class Master {
    private String name;
    public Master(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void feed(Animal animal, Food food) {
        System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
    }
}
java 复制代码
package com.hspedu.poly_;

public class PolyObject {
    public static void main(String[] args) {
        Master master = new Master("汤姆");
        Dog dog = new Dog("大黄");
        Cat cat = new Cat("小花");
        Bone bone = new Bone("大骨头");
        Fish fish = new Fish("小鱼干");
        master.feed(cat, fish);
    }
}

多态的注意事项和细节1

多态的前提是:两个对象(类)存在继承关系

1、多态的向上转型

(1)本质:父类的引用指向了子类对象

(2)语法:父类类型 引用名 = new 子类类型();

(3)特点:编译类型看左边,运行类型看右边。

(4)向上转型调用方法的规则:可以调用父类中的所有成员(需遵守访问权限),但是不能调用子类中特有成员。最终运行效果看子类的具体体现,即调用方法时,按照子类开始查找方法(和前面的方法调用规则一致)。

也就是,在编译阶段能调用哪些成员,是由编译类型来决定的,但最终的运行效果看运行类型。

2、多态的向下转型

(1)语法:子类类型 引用名 = (子类类型) 父类引用;

(2)只能强转父类的引用,不能强转父类的对象

(3)要求父类的引用必须指向的是当前目标类型的对象

(4)当向下转型后就可以调用子类类型中所有的成员

多态的注意事项和细节2

1、属性没有重写之说,属性的值看编译类型

2、instance of比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

java的动态绑定机制(重要)

1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

2、当调用对象属性时,没有动态绑定机制,哪里声明哪里使用

多态的应用

1、多态数组:数组的定义类型为父类类型,里面保存的实际元素为子类类型

应用实例:现有一个继承结构如下,要求创建1个Person对象、2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象的say方法

应用实例升级:如何调用子类特有的方法,比如Teacher有一个teach,Student有一个study

java 复制代码
package com.hspedu.poly_.ployarr;

public class PolyArray {
    public static void main(String[] args) {
        Person persons[] = new Person[5];
        persons[0] = new Person("jack",20);
        persons[1] = new Student("smith",18,100);
        persons[2] = new Student("linda",19,90);
        persons[3] = new Teacher("king",60,20000);
        persons[4] = new Teacher("amy",40,15000);
        for (int i = 0; i < persons.length; i++) {
            //动态绑定机制 编译类型Person,运行类型是根据根据实际情况由JVM来判断
            System.out.println(persons[i].say());
        }
        System.out.println("========================");
        for (int i = 0; i < persons.length; i++) {
            if(persons[i] instanceof Student) {
                Student student = (Student) persons[i];
                student.study();
            }else if(persons[i] instanceof Teacher) {
                Teacher teacher = (Teacher) persons[i];
                teacher.teach();
            }else if(persons[i] instanceof Person){
            }else {
                System.out.println("类型有误!");
            }
        }
    }
}

2、多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型

应用实例1:前面的主人喂食物

应用实例2:定义员工类Employee,包含姓名和月工资,以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工多了work方法,普通员工和经理类要求分别重写getAnnual方法。测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资并在main中调用该方法。测试类中添加一个方法testWork,如果是普通员工则调用work方法,如果是经理则调用manage方法。

java 复制代码
package com.hspedu.poly_.polyparameter;

public class PolyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom",2500);
        Manager milan = new Manager("milan",5000,20000);
        PolyParameter polyParameter = new PolyParameter();
        polyParameter.showEmpAnnual(tom);
        polyParameter.showEmpAnnual(milan);
        polyParameter.testWork(tom);
        polyParameter.testWork(milan);

    }
    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());
    }
    public void testWork(Employee e) {
        if(e instanceof Worker) {
            ((Worker) e).work(); //有向下转型操作
        } else if(e instanceof Manager) {
            ((Manager) e).manage();
        }else {
            System.out.println("不做处理...");
        }
    }
}

五、Super关键字

介绍:super代表父类的引用,用于访问父类的属性、方法、构造器

基本语法:

1、访问父类的属性,但不能访问父类private属性 super.属性名;

2、访问父类的方法,但不能访问父类private方法 super.方法名(参数列表);

3、访问父类的构造器,super(参数列表);只能放在构造器的第一句,只能出现一句

super带来的便利/细节:

1、调用父类构造器的好处(分工明确,父类的属性由父类初始化,子类的属性由子类初始化)

2、当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果。

this.say()等价于say(),从本类开始查找

super.say(),直接从父类开始查找

3、super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

super和this的比较

六、方法重写/覆盖(overwrite)

简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就说子类的这个方法覆盖了父类的那个方法。(不只是一层的关系)

注意事项和细节:

1、子类方法的形参列表、名称要和父类父类方法的形参列表、名称完全一样

2、子类方法的返回类型要和父类方法的返回类型一样,或者是父类返回类型的子类

3、子类方法不能缩小父类方法的访问权限(可以放大) public > protected > 默认 > private

方法重写和方法重载的比较

七、Object类详解

(一)equals方法

==和equals对比:

1、==是一个比较运算符:既可以判断基本类型又可以判断引用类型。

  • 如果判断基本类型,判断的是值是否相等; 实例:int i = 10; double d = 10.0;
  • 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象。

2、equals:是Object类中的方法,只能判断引用类型。默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String。

String类的equals方法把Object类的equals方法重写了,变成了比较两个字符串值是否相同。

举例:

应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false。

java 复制代码
package com.hspedu.object_;

public class Person {
    private String name;
    private int age;
    private char gender;

    // 重写Object的equals方法
    public boolean equals(Object obj) {
        // 如果比较两个对象是同一个对象,则直接返回true
        if(this == obj) {
            return true;
        }
        // 类型判断
        if(obj instanceof Person) {
            // 进行向下转型,因为需要得到obj的各个属性
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        return false;
    }

    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }
}

(二)hashCode方法

1、提高具有哈希结构的容器的效率

2、两个引用,如果指向的是同一个对象,则哈希值肯定是一样的

3、两个引用,如果指向的是不同对象,则哈希值是不一样的

4、哈希值主要根据地址号来的,但不能将哈希值等价于地址

5、后面在集合中hashCode需要的话,也会重写

(三)toString方法

默认返回:全类名+@+哈希值的十六进制

子类往往重写toString方法,用于返回对象的属性信息(当直接输出一个对象时,toString方法会被默认调用)

重写前:

重写:

java 复制代码
package com.hspedu.object_;

public class Monster {
    private String name;
    private String job;
    private double salary;

    public Monster(String name, String job, double salary) {
        this.name = name;
        this.job = job;
        this.salary = salary;
    }

    // 重写toString方法,输出对象属性
    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", salary=" + salary +
                '}';
    }
}
java 复制代码
package com.hspedu.object_;

public class ToString_ {
    public static void main(String[] args) {
        Monster monster = new Monster("妖怪", "巡山", 1000);
        System.out.println(monster.toString());
        System.out.println(monster);
    }
}

(三)finalize方法

1、当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。

2、什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。

3、垃圾回收机制的调用, 是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。

4、实际开发中,几乎不会用到finalize方法

java 复制代码
package com.hspedu.object_;

public class Car {
    private String name;

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

    @Override
    protected void finalize() throws Throwable {
        System.out.println("我们销毁汽车" + name);
        System.out.println("释放了某些资源");
    }
}
java 复制代码
package com.hspedu.object_;

public class Finalize_ {
    public static void main(String[] args) {
        Car car = new Car("宝马");
        // 这时car对象就是一个垃圾,垃圾回收器就会销毁对象,在销毁对象前会调用该对象的finalize方法
        // 程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源,数据库连接、打开文件...)
        // 如果程序员不重写finalize,那么就会调用Object类的finalize,即默认处理
        car = null;
        System.gc(); //主动调用垃圾回收器
        System.out.println("程序退出了...");
    }
}

八、断点调试(debug)

1、在断点调试过程中是运行状态,是以对象的运行类型来执行的

2、断点调试的快捷键

F7(跳入):跳入方法内

F8(跳过) :逐行执行代码

shift+F8(跳出) :跳出方法

F9:resume,执行到下一个断点

演示1:

演示2:

相关推荐
考虑考虑4 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261354 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊5 小时前
Java学习第22天 - 云原生与容器化
java
渣哥7 小时前
原来 Java 里线程安全集合有这么多种
java
间彧7 小时前
Spring Boot集成Spring Security完整指南
java
间彧8 小时前
Spring Secutiy基本原理及工作流程
java
Java水解9 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆11 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学11 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole11 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端