文章目录
- 一、IDEA
- 1.1 IDEA下载安装
- 1.2 IDEA的使用
- 1.3 IDEA常用的快捷键
- 二、包
- 2.1 包的基本介绍
- 2.2 包的本质分析
- 2.3 包的命名
- 2.4 常用的包
- 2.5 如何引入包
- 三、访问修饰符
- 四、面向对象的三大特征
- 4.1 封装
- 4.2 继承
- 4.2.1 为什么需要继承
- 4.2.2 继承的基本介绍
- 4.2.3 继承的深入讨论/细节问题
- 4.2.4 继承的本质分析(内存分析,重要)
- 4.3 super关键字
- 4.4 super与this的比较
- 4.5 方法重写/覆盖(override)
- 4.6 方法重写与重载比较
- 4.7 多态
- 4.7.1 基本介绍
- 4.7.2 多态注意事项和细节讨论
- 4.7.3 java的动态绑定机制(非常非常重要)
- 4.7.4 多态的应用
- 五、Object类详解
- 5.1 equals方法
- 5.2 hashCode方法
- 5.3 toString()方法
- 5.4 finalize方法
- 六、断点调试
- 七、细节知识
- 7.1 访问修饰符
- 7.2 继承的细节
- 7.3 继承的本质分析(内存分析,重要)
- 7.4 super细节
- 7.5 方法重写的细节
- 7.6 多态的理解
- 7.7 java的动态绑定机制(非常非常重要)
一、IDEA
1.1 IDEA下载安装
1.2 IDEA的使用
1.3 IDEA常用的快捷键
二、包
2.1 包的基本介绍
2.2 包的本质分析
2.3 包的命名
2.4 常用的包
2.5 如何引入包
建议:我们需要使用哪个类就导入哪个类,不建议使用 *
导入
三、访问修饰符
注:上述多个访问范围之间是或的关系(如:protected修饰的属性或方法可以被 同类
或 同包
或 子类
访问);另外 同包
指的是 完全相同的包路径
(子包路径不算相同)
四、面向对象的三大特征
- 基本介绍
面向对象编程有三大特征:封装
、继承
和多态
4.1 封装
-
基本介绍
-
封装的理解和好处
-
封装的实现步骤
-
快速入门
public class Hello {
public static void main(String[] args) {
Person person = new Person();
person.setAge(900);
}
}
class Person {
public String name;
private int age;
private double salary;
private String job;
public Person() {
}
public Person(String name, int age, double salary, String job) {
setAge(age);
setJob(job);
setName(name);
setSalary(salary);
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
return;
}
System.out.println("输入的名字长度必须在2-6之间");
}
public void setAge(int age) {
if (age >= 1 && age <= 120) {
this.age = age;
return;
}
System.out.println("年龄必须在1-120");
}
public void setSalary(double salary) {
this.salary = salary;
}
public void setJob(String job) {
this.job = job;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
public String getJob() {
return job;
}
}
4.2 继承
4.2.1 为什么需要继承
4.2.2 继承的基本介绍
4.2.3 继承的深入讨论/细节问题
// 基础父类
package com.extends_;
public class Base {
// 4个属性
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public int getN4() { // 构建公共方法使子类可以访问私有属性
return this.n4;
}
public Base() {
}
public void test100() {
System.out.println("test100()....");
}
protected void test200() {
System.out.println("test200()....");
}
void test300() {
System.out.println("test300()....");
}
private void test400() {
System.out.println("test400()....");
}
}
// 子类(与父类同包)
package com.extends_;
public class Sub extends Base {
public Sub() {
}
public void sayOk() {
// 非私有的属性和方法可以在子类直接访问
// 私有属性和方法不能在子类直接访问(n4无法直接访问)
System.out.println(n1 + "" + n2 + "" + n3);
int n4 = getN4();// 通过父类的公共方法对私有属性进行访问
System.out.println("n4:"+n4);
test100();
test200();
test300();
// test400(); 无法直接访问
}
}
- 注意第6点,使用了
this()
后则编译器不会默认添加一个super()
4.2.4 继承的本质分析(内存分析,重要)
public class ExtendsTest{
public static void main(String[] args){
Son son = new son(); // 内存的布局
System.out.println(son.age); // 访问父类中的属性age
}
}
class GrandPa{
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa{
String name = "大头爸爸";
int age = 39;
}
class Son extends Father{
String name = "大头儿子";
}
- 执行流程:
- 在
方法区
中加载类信息
,顺序由继承关系决定(Object
-->GrandPa
-->Father
-->Son
)并建立类之间的关系 - 在堆中创建Son的实例,按
继承关系
创建属性
并初始化
(各父类中的变量会存储在独立的二级堆空间
中) - 将Son实例的引用赋值给
main方法栈
中的son引用变量
- 若使用son调用
属性
或方法
,则遵守以下原则:- 首先看子类是否有该
属性
(方法
) - 如果子类有这个
属性
(方法
),并可以访问,则返回信息 - 如果子类没有这个
属性
(方法
),就看父类有没有这个属性
(方法
)(如果父类有该属性
(方法
),并可以访问,就返回信息…) - 如果父类没有就按照(3)的规则,继续找上级父类,直到Object。注意:如果某一父类中有该
属性
(方法
)但由于访问修饰符限制(如:private)无法直接访问,则java编译器会停止向其父类寻找,直接返回无法访问的编译错误
- 首先看子类是否有该
- 在
4.3 super关键字
-
基本介绍
注意:super
不是一个对象引用,super
是一个指示java编译器调用父类实例域
的特殊关键字
-
super的细节
public class A{
public void say(){
System.out.println("A say~~");
}
}
public class B extends A{
public void test(){
// say();
// this.say();
super.say();
}
}
- 在B的方法中调用
say()
(与this.say()
等同)的执行流程如下:- 首先看子类是否有该
属性
(方法
) - 如果子类有这个
属性
(方法
),并可以访问,则返回信息 - 如果子类没有这个
属性
(方法
),就看父类有没有这个属性
(方法
)(如果父类有该属性
(方法
),并可以访问,就返回信息…) - 如果父类没有就按照(3)的规则,继续找上级父类,直到Object。注意:如果某一父类中有该
属性
(方法
)但由于访问修饰符限制(如:private)无法直接访问,则java编译器会停止向其父类寻找,直接返回无法访问的编译错误
注意:使用super.say()
会跳过上述第一步,直接从其父类开始查找
- 首先看子类是否有该
4.4 super与this的比较
4.5 方法重写/覆盖(override)
-
基本介绍
-
方法重写注意事项和使用细节
4.6 方法重写与重载比较
4.7 多态
4.7.1 基本介绍
注意 编译类型
与 运行类型
的概念
4.7.2 多态注意事项和细节讨论
- 调用方法使用
运行类型
,直接访问属性使用编译类型
public class Animal{
public void eat(){
System.out.println("...");
}
}
class Cat extends Animal{
public void eat(){ // 方法重写
System.out.println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){ // 方法重写
System.out.println("吃骨头");
}
}
public class Test{
public static void main(String[] args){
Animal animal = new Cat(); // 父类的引用指向了子类的实例
// 可以调用父类中的所有成员(需要遵守访问权限)
// 但是不能调用子类的特有的成员 (因为需要通过java的编译)
// 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// 最终的运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法
// 然后调用,规则和我们前面讲的方法调用规则一致
animal.eat(); // 调用重写的实现
}
}
- 注
null
数据也可以使用instanceof
,其结果为false
public class Test{
public static void main(String[] args){
BB bb = new BB();
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
// aa的编译类型是AA,运行类型是BB
AA aa = new BB();
System.out.println(aa.n); // "a",说明访问的是编译类型对应的属性值
System.out.println(aa instanceof AA); // true
System.out.println(aa instanceof BB); // true,说明 instanceof 判断的是aa的运行类型是否是BB
}
}
class AA{
String n = 'a';
}
class BB extends AA{
String n = 'b';
}
4.7.3 java的动态绑定机制(非常非常重要)
- 当调用对象
方法
的时候,该方法会和该对象的内存地址/运行类型
绑定 - 当调用对象
属性
时,没有动态绑定机制
,哪里声明,哪里使用
public class Test{
public static void main(String[] args){
A a = new B(); // 向上转型
// 动态绑定机制:
// (1)调用 B 类继承的sum方法(sum() 与 B 的实例绑定,这里 this --> new B());
// (2)其中getI() (同this.getI())则调用B中重写的方法;
// (3)B中的getI()方法中的 i (同this.i)没有动态绑定机制则返回B的成员变量I
System.out.println(a.sum()); // 30
// 动态绑定机制:
// (1)调用 B 类继承的sum1方法(sum1() 与 B 的实例绑定,这里 this --> new B());
// (3)其中的 i (同this.i)没有动态绑定机制则返回A的成员变量I
System.out.println(a.sum1()); // 20
}
}
class A{ // 父类
public int i = 10;
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
class B{ // 子类
public int i = 20;
public int getI(){
return i;
}
}
4.7.4 多态的应用
五、Object类详解
5.1 equals方法
Object
类的equals
方法实现的是判断是否是同一个对象
// 自定义equals方法
public class Person{
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender){
this.name = name;
this.age = age;
this.gender = gender;
}
public boolean equals(Obejct obj){
if (obj == this){
return true;
}
if(obj instanceof Person){
Person pObj = (Person) obj ;
return this.name.equals(pObj .name) && this.age == pObj.age && this.gender == pObj.gender;
}
return false;
}
}
5.2 hashCode方法
5.3 toString()方法
5.4 finalize方法
六、断点调试
七、细节知识
7.1 访问修饰符
成员变量
和方法
可以被:public
、protected
、默认
、private
修饰- 类只能被:
public
和默认
修饰
注:上述多个访问范围之间是或的关系(如:protected修饰的属性或方法可以被 同类
或 同包
或 子类
访问)。另外 同包
指的是 完全相同的包路径
(子包路径不算相同)
7.2 继承的细节
- 注意第6点,使用了
this()
后则编译器不会默认添加一个super()
7.3 继承的本质分析(内存分析,重要)
public class ExtendsTest{
public static void main(String[] args){
Son son = new son(); // 内存的布局
}
}
class GrandPa{
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa{
String name = "大头爸爸";
int age = 39;
}
class Son extends Father{
String name = "大头儿子";
}
- 执行流程:
- 在
方法区
中加载类信息
,顺序由继承关系决定(Object
-->GrandPa
-->Father
-->Son
)并建立类之间的关系 - 在堆中创建Son的实例,按
继承关系
创建属性
并初始化
(各父类中的变量会存储在独立的二级堆空间
中) - 将Son实例的引用赋值给
main方法栈
中的son引用变量
- 若使用son调用
属性
或方法
,则遵守以下原则:- 首先看子类是否有该
属性
(方法
) - 如果子类有这个
属性
(方法
),并可以访问,则返回信息 - 如果子类没有这个
属性
(方法
),就看父类有没有这个属性
(方法
)(如果父类有该属性
(方法
),并可以访问,就返回信息…) - 如果父类没有就按照(3)的规则,继续找上级父类,直到Object。注意:如果某一父类中有该
属性
(方法
)但由于访问修饰符限制(如:private)无法直接访问,则java编译器会停止向其父类寻找,直接返回无法访问的编译错误
- 首先看子类是否有该
- 在
7.4 super细节
注意:super
不是一个对象引用(不能赋值给引用变量),super
是一个指示java编译器调用父类实例域
的特殊关键字
public class A{
public void say(){
System.out.println("A say~~");
}
}
public class B extends A{
public void test(){
// say();
// this.say();
super.say();
}
}
- 在B的方法中调用
say()
(与this.say()
等同)的执行流程如下:- 首先看子类是否有该
属性
(方法
) - 如果子类有这个
属性
(方法
),并可以访问,则返回信息 - 如果子类没有这个
属性
(方法
),就看父类有没有这个属性
(方法
)(如果父类有该属性
(方法
),并可以访问,就返回信息…) - 如果父类没有就按照(3)的规则,继续找上级父类,直到Object。注意:如果某一父类中有该
属性
(方法
)但由于访问修饰符限制(如:private)无法直接访问,则java编译器会停止向其父类寻找,直接返回无法访问的编译错误
注意:使用super.say()
会跳过上述第一步,直接从其父类开始查找
- 首先看子类是否有该
7.5 方法重写的细节
7.6 多态的理解
- 注意
编译类型
与运行类型
的概念 - 调用方法使用
运行类型
,直接访问属性使用编译类型
public class Animal{
public void eat(){
System.out.println("...");
}
}
class Cat extends Animal{
public void eat(){ // 方法重写
System.out.println("吃鱼");
}
}
class Dog extends Animal{
public void eat(){ // 方法重写
System.out.println("吃骨头");
}
}
public class Test{
public static void main(String[] args){
Animal animal = new Cat(); // 父类的引用指向了子类的实例
// 可以调用父类中的所有成员(需要遵守访问权限)
// 但是不能调用子类的特有的成员 (因为需要通过java的编译)
// 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// 最终的运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法
// 然后调用,规则和我们前面讲的方法调用规则一致
animal.eat(); // 调用重写的实现
}
}
public class Test{
public static void main(String[] args){
BB bb = new BB();
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
// aa的编译类型是AA,运行类型是BB
AA aa = new BB();
System.out.println(aa.n); // "a",说明访问的是编译类型对应的属性值
System.out.println(aa instanceof AA); // true
System.out.println(aa instanceof BB); // true,说明 instanceof 判断的是aa的运行类型是否是BB
}
}
class AA{
String n = 'a';
}
class BB extends AA{
String n = 'b';
}
7.7 java的动态绑定机制(非常非常重要)
- 当调用对象
方法
的时候,该方法会和该对象的内存地址/运行类型
绑定 - 当调用对象
属性
时,没有动态绑定机制
,哪里声明,哪里使用
public class Test{
public static void main(String[] args){
A a = new B(); // 向上转型
// 动态绑定机制:
// (1)调用 B 类继承的sum方法(sum() 与 B 的实例绑定,这里 this --> new B());
// (2)其中getI() (同this.getI())则调用B中重写的方法;
// (3)B中的getI()方法中的 i (同this.i)没有动态绑定机制则返回B的成员变量I
System.out.println(a.sum()); // 30
// 动态绑定机制:
// (1)调用 B 类继承的sum1方法(sum1() 与 B 的实例绑定,这里 this --> new B());
// (3)其中的 i (同this.i)没有动态绑定机制则返回A的成员变量I
System.out.println(a.sum1()); // 20
}
}
class A{ // 父类
public int i = 10;
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
class B{ // 子类
public int i = 20;
public int getI(){
return i;
}
}