五.面向对象编程(中级)
1.IDEA使用技巧和经验(pycharm也类似)
-
必熟快捷键!自定义可以在file -> settings -> Keymap中设置
-
删除当前行(自定义) ctrl + D
-
剪切当前行 ctrl + X
-
复制当前行 (自定义) Ctrl + alt +向下光标
-
补全代码 alt+/
-
导入该行需要的类 先配置auto import 然后使用alt+enter
-
快速格式化代码 Ctrl + alt + L
-
快速运行程序 (自定义) alt +R
-
生成构造器 alt +insert
-
查看类的层级关系 ctrl +H (试试)
-
定位到方法 光标放方法上,ctrl +B
-
自动分配变量名 在分配空间后边.var (试试)
-
-
模版
- 可以在file -> settings -> editor -> Live templates中查看有哪些模版以及自定义模板
2.包
- 包的命名
- 一般是小写字母+小圆点
- com.公司名.形目名.业务模块名
- 常用的包
- java.lang 基本包,默认引入
- java.util 系统提供的工具包,工具类,使用Scanner
- java.net 网络包
- java.awt Java界面开发,GUI
import java.util.Scanner;
import java.util.*;
//使用哪个类就导入哪个,不要全部导入
- package的作用是声明当前类所在的包,需要放在类的最上面;import语句放到package下边,一个类中只能有一个package
3.访问修饰符
- 类别介绍
- public:对外公开
- protected :对子类和同一个包中的类公开
- 默认 :向同一个包的类公开
- private :只有类本身可以访问
- 修饰符可以修饰类中的属性、方法以及类
- 四种修饰符都可以修饰属性方法
- 类只能用默认和public修饰!!!
4.封装
-
好处
- 隐藏实现细节,用方法调用即可
- 可以对数据进行验证,保证安全合理
-
封装实现步骤
-
先对属性私有化(不能直接修改属性)
-
提供一个公共的set方法,用于对属性判断并赋值
-
提供一个公共的get方法,用于获取属性的值
-
IDEA使用快捷构造set和get方法:alt+fn+insert
-
-
封装会在修改是进行保护和判断,但是使用构造器初始化一个对象时会越过set方法的保护和判断,所以可以把set封装到构造器中
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
//我们可以将set方法写在构造器中,这样仍然可以验证
setName(name);
setAge(age);
setSalary(salary);
}
5.继承
- **好处:**代码的可复用性提高了
- 子类继承了所有的属性和方法,非私有的属性和方法可以直接访问,但是私有的属性和方法不能在子类直接访问,要通过父类提供公共的方法访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类构造器中用super去指定父类的哪个构造器完成对父类初始化工作,否则编译会报错
public class Sub extends Bash{
public Sub() {
super();//这行语句调用父类Bash的无参构造器,不写系统会自己默认调用,写不写一样
System.out.printf("sub()...");
}
}
- 如果希望指定去调用弗雷德额某个构造器,则显式的调用一下
super(参数列表)
-
super使用时必须放在构造器第一行
-
super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
-
java所有类都是Object,是所有类的基类(使用ctrl+H查看继承关系)
-
父类构造器的调用不限于直接父类(即上一级父类),将会一致往上追溯直到Object类(顶级父类)
-
(star)子类在java中最多只能 继承一个父类,即java中是单继承机制,那么怎么让A类同时继承B类和C类?可以先用B继承C,再用A继承B
-
不能滥用继承,子类父类之间必须满足is-a的逻辑关系
-
继承的本质分析(重要,韩顺平293集)
public class ExtendsTheory { public static void main(String[] args) { Son son = new Son();//内存的布局,布局图见下图 //?-> 这时请大家注意,要按照查找关系来返回信息 //(1) 首先看子类是否有该属性 //(2) 如果子类有这个属性,并且可以访问,则返回信息 //(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..) //(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到Object... System.out.println(son.name);//返回就是大头儿子 //System.out.println(son.age);//在age不是私有的时候可以访问,返回的就是39 //System.out.println(son.getAge());//在age是私有的时候,使用公有的get调用,返回的就是39 System.out.println(son.hobby);//返回的就是旅游 } } class GrandPa { //爷类 String name = "大头爷爷"; String hobby = "旅游"; } class Father extends GrandPa {//父类 String name = "大头爸爸"; private int age = 39; public int getAge() { return age; } } class Son extends Father { //子类 String name = "大头儿子"; }
- 上述代码内存图
6.super、方法重写
super
- super代表父类的引用,用于访问父类的属性、方法、构造器
- 基本语法:
- 访问父类除了private的属性 super.属性
- 访问父类除了private的方法 super.方法(参数列表)
- 访问父类的构造器 super(参数列表) 只能放在构造器的第一行
- 调用父类构造器的好处
- super的访问不限于父类,也可以调用爷爷类,super遵循就近原则,但如果父类中的是private属性或者方法,则直接截断,不会再继续调用爷爷类的public属性和方法
super和this的比较
方法重写/覆盖(override)
- **定义:**子类有一个方法和父类的某个方法的名称、返回类型、参数一样,就说子类的这个方法覆盖了父类的方法
- 子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类,比如父类返回Object,子类返回String就是可以的,反过来就不行
- 子类方法不能缩小父类方法的访问权限,但是放大可以(子类访问权限>=父类访问权限)
方法重写、重载的区别
7.多态
-
多态是指方法或者对象具有多种形态,多态是建立在封装和集成的基础之上的
-
方法的多态
- 重写和重载就体现了多态
-
对象的多态(重难点)
- 编译类型看定义时=左边,运行类型看=号右边
//假设Dog继承于父类Animal Animal animal = new Dog(); //此时编译类型是Animal,运行类型是Dog
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时就确定了,不能再改变
- 运行类型是可以变化的
- 访问属性看编译类型,访问方法看运行类型
-
多态的细节
-
前提:两个对象(类)存在继承关系
-
向上转型
-
本质:父类的引用指向了子类的对象
-
Animal animal = new Dog();
-
特点:(1)对象可以调用父类中的所有成员(需要遵守访问权限) (2)对象不能调用子类中的特有成员 (3)最终运行效果看子类中的具体实现
-
-
向下转型
-
Animal animal = new Dog(); Dog dog = (Dog) animal;//可以跟强制转换进行类比
-
只能强转父类的引用,不能强转父类的对象
-
要求父类的引用必须指向的是当前目标类型的对象
-
当向下转型后,可以调用子类类型中所有的成员
-
-
-
java的动态绑定机制(重要)
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定(不管当前执行的方法是编译类型里的还是运行类型里的,只要再递归调用另一个方法,就去运行类里面找)
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用 (当前需要用的属性声明在哪个类中,就用哪个类的,不用考虑是编译类型还是运行类型。也就是说属性没有动态绑定机制)
-
多态的应用
-
多态数组
- 指的是数组的定义类型(编译类型)为父类类型,而里边实际保存的元素类型为子类类型(运行类型)
- 应用实例:现有一个继承结构如下,Person<-Student ,Person<-Teacher
- 要求创建1个Person对象、2个Student 对象和2个Teacher对象, 统一放在数组中,并调用每个对象say方法(三个类都有)
- 要求访问Student 对象和Teacher对象独有的方法(Student 中有score() ,Teacher中有salary())(由多态的向上转型可知,如果不对对象执行操作,直接调用子类中特有方法,是找不到方法的,需要先进行向下转型)
//向上转型 Person[] persons = new Person[5]; persons[0] = new Person("jack", 20); persons[1] = new Student("mary", 18, 100); persons[2] = new Student("smith", 19, 30.1); persons[3] = new Teacher("scott", 30, 20000); persons[4] = new Teacher("king", 50, 25000); //循环遍历多态数组,调用say for (int i = 0; i < persons.length; i++) { // person[i] 编译类型是 Person ,运行类型是是根据实际情况有JVM来判断 System.out.println(persons[i].say());//动态绑定机制 //使用 类型判断 + 向下转型. if(persons[i] instanceof Student) {//判断person[i] 的运行类型是不是Student Student student = (Student)persons[i];//向下转型 student.study(); //小伙伴也可以使用一条语句 ((Student)persons[i]).study(); } else if(persons[i] instanceof Teacher) { Teacher teacher = (Teacher)persons[i]; teacher.teach(); } else if(persons[i] instanceof Person){ //System.out.println("你的类型有误, 请自己检查..."); } else { System.out.println("你的类型有误, 请自己检查..."); } }
-
多态参数
- 方法定义的形参类型为父类类型,实参类型允许为子类类型
-
8.Object详解
-
属于java.lang包(默认引入的)
-
该类下的几个方法
-
equals()
- ==既可以判断基本类型(值是否相等)、也可以判断引用类型(地址是不是相等,是不是一个对象)
- equals只能用于判断引用类型,也就是是否指向同一个地址空间
- 如果想判断两个对象内的值是否完全相等,可以重写equals方法(源码String中已经把equals重写了)
-
hashCode()
- 返回该对象的哈希码值
- 结论(后边会详细讲)
- 为了提高哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!不能完全将哈希值等价于地址
- 后面在集合中,需要hashcode的话,也会重写
-
toString()
- 默认返回全类名+@+哈希值的十六进制
//源码 //getClass().getName()返回类的全类名(包名+类名) public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
- 可以重写toString方法,实现自己想要的输出
-
9.断点调试
- F7 跳入
- F8 跳过
- shift+F8 跳出
- F9 下一个断点(断点可以在debug的过程中设置)