• 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包所有都引入,但建议需要使用什么类就导入什么类)

    1. package com.hspedu.pkg;
    2. import java.util.Arrays;
    3. import java.util.Scanner;
    4. //import java.util.*;
    5. public class Import01 {
    6. public static void main(String[] args) {
    7. int arr[] = {-1, 20, 2, 13, 3};
    8. Arrays.sort(arr);
    9. for (int i = 0; i < arr.length; i++) {
    10. System.out.print(arr[i] + "\t");
    11. }
    12. }
    13. }

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

    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字符。

    1. package com.hspedu.encap;
    2. public class Encapsulation01 {
    3. public static void main(String[] args) {
    4. Person person = new Person();
    5. person.setName("jack");
    6. person.setAge(20);
    7. person.setSalary(30000);
    8. System.out.println(person.info());
    9. //如果使用构造器指定属性
    10. Person smith = new Person("smith", 80, 50000);
    11. System.out.println("======smith信息=====");
    12. System.out.println(smith.info());
    13. }
    14. }
    15. class Person {
    16. public String name;
    17. private int age;
    18. private double salary;
    19. public Person() {
    20. }
    21. public Person(String name, int age, double salary) {
    22. // this.name = name;
    23. // this.age = age;
    24. // this.salary = salary;
    25. // 可以将set方法写在构造器中,这样仍然可以验证
    26. this.setName(name);
    27. this.setAge(age);
    28. this.setSalary(salary);
    29. }
    30. public String getName() {
    31. return name;
    32. }
    33. public void setName(String name) {
    34. // 加入对数据的校验
    35. if(name.length() >=2 && name.length() <= 6){
    36. this.name = name;
    37. } else {
    38. System.out.println("名字长度不对,需要(2-6)个字符,给默认名字");
    39. this.name = "无名";
    40. }
    41. }
    42. public int getAge() {
    43. return age;
    44. }
    45. public void setAge(int age) {
    46. // 加入对数据的校验
    47. if(age >= 1 && age <= 120){
    48. this.age = age;
    49. } else {
    50. System.out.println("你设置的年龄不对,需要在(1-120),给默认年龄为18");
    51. this.age = 18;
    52. }
    53. }
    54. public double getSalary() {
    55. // 这里可以增加对当前对象的权限判断
    56. return salary;
    57. }
    58. public void setSalary(double salary) {
    59. this.salary = salary;
    60. }
    61. public String info() {
    62. return "信息为 name=" + name + " age=" + age + " salary=" + salary;
    63. }
    64. }

    (二)继承

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

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

    基本语法:

    class 子类 extends 父类 {

    }

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

    2、父类又叫超类、基类

    3、子类又叫派生类

    快速入门:

    1. package com.hspedu.extend_;
    2. public class Student {
    3. public String name;
    4. public int age;
    5. private double score;
    6. public void setScore(double score) {
    7. this.score = score;
    8. }
    9. public void showInfo() {
    10. System.out.println("学生名=" + name + " 年龄=" + age + " 成绩=" + score);
    11. }
    12. }
    1. package com.hspedu.extend_;
    2. public class Pupil extends Student{
    3. public void testing() {
    4. System.out.println("小学生" + name + "正在考小学数学...");
    5. }
    6. }
    1. package com.hspedu.extend_;
    2. public class Graduate extends Student{
    3. public void testing() {
    4. System.out.println("大学生" + name + "正在考大学数学...");
    5. }
    6. }
    1. package com.hspedu.extend_;
    2. public class Extends01 {
    3. public static void main(String[] args) {
    4. Pupil pupil = new Pupil();
    5. pupil.name = "小红";
    6. pupil.age = 10;
    7. pupil.testing();
    8. pupil.setScore(60);
    9. pupil.showInfo();
    10. System.out.println("=============");
    11. Graduate graduate = new Graduate();
    12. graduate.name = "大红";
    13. graduate.age = 20;
    14. graduate.testing();
    15. graduate.setScore(100);
    16. graduate.showInfo();
    17. }
    18. }

    继承给编程带来了便利:

    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

    快速入门:

    1. package com.hspedu.poly_;
    2. public class Master {
    3. private String name;
    4. public Master(String name) {
    5. this.name = name;
    6. }
    7. public String getName() {
    8. return name;
    9. }
    10. public void setName(String name) {
    11. this.name = name;
    12. }
    13. public void feed(Animal animal, Food food) {
    14. System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
    15. }
    16. }
    1. package com.hspedu.poly_;
    2. public class PolyObject {
    3. public static void main(String[] args) {
    4. Master master = new Master("汤姆");
    5. Dog dog = new Dog("大黄");
    6. Cat cat = new Cat("小花");
    7. Bone bone = new Bone("大骨头");
    8. Fish fish = new Fish("小鱼干");
    9. master.feed(cat, fish);
    10. }
    11. }

    多态的注意事项和细节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

    1. package com.hspedu.poly_.ployarr;
    2. public class PolyArray {
    3. public static void main(String[] args) {
    4. Person persons[] = new Person[5];
    5. persons[0] = new Person("jack",20);
    6. persons[1] = new Student("smith",18,100);
    7. persons[2] = new Student("linda",19,90);
    8. persons[3] = new Teacher("king",60,20000);
    9. persons[4] = new Teacher("amy",40,15000);
    10. for (int i = 0; i < persons.length; i++) {
    11. //动态绑定机制 编译类型Person,运行类型是根据根据实际情况由JVM来判断
    12. System.out.println(persons[i].say());
    13. }
    14. System.out.println("========================");
    15. for (int i = 0; i < persons.length; i++) {
    16. if(persons[i] instanceof Student) {
    17. Student student = (Student) persons[i];
    18. student.study();
    19. }else if(persons[i] instanceof Teacher) {
    20. Teacher teacher = (Teacher) persons[i];
    21. teacher.teach();
    22. }else if(persons[i] instanceof Person){
    23. }else {
    24. System.out.println("类型有误!");
    25. }
    26. }
    27. }
    28. }

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

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

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

    1. package com.hspedu.poly_.polyparameter;
    2. public class PolyParameter {
    3. public static void main(String[] args) {
    4. Worker tom = new Worker("tom",2500);
    5. Manager milan = new Manager("milan",5000,20000);
    6. PolyParameter polyParameter = new PolyParameter();
    7. polyParameter.showEmpAnnual(tom);
    8. polyParameter.showEmpAnnual(milan);
    9. polyParameter.testWork(tom);
    10. polyParameter.testWork(milan);
    11. }
    12. public void showEmpAnnual(Employee e) {
    13. System.out.println(e.getAnnual());
    14. }
    15. public void testWork(Employee e) {
    16. if(e instanceof Worker) {
    17. ((Worker) e).work(); //有向下转型操作
    18. } else if(e instanceof Manager) {
    19. ((Manager) e).manage();
    20. }else {
    21. System.out.println("不做处理...");
    22. }
    23. }
    24. }

    五、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。

    1. package com.hspedu.object_;
    2. public class Person {
    3. private String name;
    4. private int age;
    5. private char gender;
    6. // 重写Object的equals方法
    7. public boolean equals(Object obj) {
    8. // 如果比较两个对象是同一个对象,则直接返回true
    9. if(this == obj) {
    10. return true;
    11. }
    12. // 类型判断
    13. if(obj instanceof Person) {
    14. // 进行向下转型,因为需要得到obj的各个属性
    15. Person p = (Person)obj;
    16. return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
    17. }
    18. return false;
    19. }
    20. public Person(String name, int age, char gender) {
    21. this.name = name;
    22. this.age = age;
    23. this.gender = gender;
    24. }
    25. public String getName() {
    26. return name;
    27. }
    28. public void setName(String name) {
    29. this.name = name;
    30. }
    31. public int getAge() {
    32. return age;
    33. }
    34. public void setAge(int age) {
    35. this.age = age;
    36. }
    37. public char getGender() {
    38. return gender;
    39. }
    40. public void setGender(char gender) {
    41. this.gender = gender;
    42. }
    43. }

    (二)hashCode方法

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

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

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

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

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

     (三)toString方法

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

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

    重写前:

    重写:

    1. package com.hspedu.object_;
    2. public class Monster {
    3. private String name;
    4. private String job;
    5. private double salary;
    6. public Monster(String name, String job, double salary) {
    7. this.name = name;
    8. this.job = job;
    9. this.salary = salary;
    10. }
    11. // 重写toString方法,输出对象属性
    12. @Override
    13. public String toString() {
    14. return "Monster{" +
    15. "name='" + name + '\'' +
    16. ", job='" + job + '\'' +
    17. ", salary=" + salary +
    18. '}';
    19. }
    20. }
    1. package com.hspedu.object_;
    2. public class ToString_ {
    3. public static void main(String[] args) {
    4. Monster monster = new Monster("妖怪", "巡山", 1000);
    5. System.out.println(monster.toString());
    6. System.out.println(monster);
    7. }
    8. }

    (三)finalize方法 

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

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

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

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

    1. package com.hspedu.object_;
    2. public class Car {
    3. private String name;
    4. public Car(String name) {
    5. this.name = name;
    6. }
    7. @Override
    8. protected void finalize() throws Throwable {
    9. System.out.println("我们销毁汽车" + name);
    10. System.out.println("释放了某些资源");
    11. }
    12. }
    1. package com.hspedu.object_;
    2. public class Finalize_ {
    3. public static void main(String[] args) {
    4. Car car = new Car("宝马");
    5. // 这时car对象就是一个垃圾,垃圾回收器就会销毁对象,在销毁对象前会调用该对象的finalize方法
    6. // 程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源,数据库连接、打开文件...)
    7. // 如果程序员不重写finalize,那么就会调用Object类的finalize,即默认处理
    8. car = null;
    9. System.gc(); //主动调用垃圾回收器
    10. System.out.println("程序退出了...");
    11. }
    12. }

     八、断点调试(debug)

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

    2、断点调试的快捷键

    F7(跳入):跳入方法内

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

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

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

    演示1:

    演示2:

     

     

     

  • 相关阅读:
    跨境电商品牌如何制定成功的聊天机器人策略
    在Ubuntu系统上实现免费电脑IP更改
    数据库中存储bool对象的数据,使用Bit和Integer有什么区别,性能上有多大的差异
    代码随想录day31|开始贪心咯|贪心理论|455.分发饼干|376. 摆动序列|53. 最大子序和|复习day2|Golang
    java毕业设计打车拼车系统服务端Mybatis+系统+数据库+调试部署
    永热爱 敢向前 | Paraverse平行云的2023 年终总结
    QT DAY4
    面试时必问的五大问题
    技术面试面面观
    数据结构—数组
  • 原文地址:https://blog.csdn.net/weixin_45827876/article/details/134274512