Java面向对象(二)

一、类和对象

1. 基本概念

  • 类是对现实生活中一类具有共同属性和行为的事物的抽象
  • 类由属性行为组成
    • 属性:事物的特征。例如:学生姓名,年龄,性别
    • 行为:事物能执行的操作。例如:上课,运动,打游戏
  • 类是对事物的一种描述,对象是啥事物的具体存在。例如:学生类和具体的某个学生对象
  • 类是对象的数据类型,类是具有共同属性和行为的一组对象的集合
  • 类是对象的模板,对象是类的实例,模板决定了实体

2. 类和对象的定义

2.1 类

  • 属性:在类中通过成员变量来体现(类中方法外的变量)
  • 行为:在类中通过成员方法来体现
修饰符 class 类名 {
	// 成员变量
	数据类型 变量;
	private int age;
	// 成员方法
	方法1;
	public void study{}
	//构造方法
	public 类名()
}

2.2 对象

//创建对象
类名 对象名 = new 类名();
Student stu = new Student();
//调用成员
对象名.成员变量
stu.age;
对象名.成员方法();
stu.study();

2.3 类定义补充

  • 类名使用大驼峰命名法,见名知意
  • 一个 java 文件中可以定义多个 class 类,但是只能一个类是 public 修饰的,public 修饰的类名必须与文件名相同
    实际开发建议一个文件定义一个 class 类
2.3.1 测试类

用来检查其他类是否书写正确,带有 main 方法的类,是程序的入口

  1. 可以在测试类中创建 JavaBean 类的对象使用
  2. 可以在测试类中直接使用工具类的方法完成一些功能
public static void main(String[] args) {
}
//public:被JVM调用,访问权限足够大
//static:被JVM调用,不用创建对象,直接类名访问,因为main方法是静态的,所以测试类方法也需要是静态的
//void:被JVM调用,不需要给JVM返回值
//main:一个通用的名称,虽然不是关键字,但是被JVM识别
//String[] args:以前用于接收键盘录入数据的,为了向下兼容所以保留
2.3.2 JavaBean 类

用来描述一类事物的类,叫做 JavaBean

  1. 类名需要见名知意,大驼峰命名
  2. 所有成员变量都是私有的,使用 private 修饰
  3. 提供至少两个公共的构造方法
    • 无参构造方法
    • 带全部参数构造方法
  4. 对每个成员变量提供两个公共方法
    • setXxx() :用来设置属性 Xxx 的值
    • getXxx() :用来获取属性 Xxx 的值(若属性类型为 boolean,则方法名为 isXxx()
2.3.3 工具类

工具类是实现一些功能的类

  1. 类名需要见名知意,大驼峰命名
  2. 私有化构造方法
  3. 方法定义为静态

3. 封装

3.0 引入

当传参数很多的时候很麻烦,有了封装只需要传一个对象就可以

//没有封装
public static void show(id, name, age, sex, school) {}
//有封装
public static void show(student) {}

3.1 封装思想

是面向对象三大特征之一(封装,继承,多态)
对象代表什么,就得封装对应的数据,并提供数据对应的行为

3.2 权限修饰符

作用:使用权限修饰符控制成员能被访问的范围

实际开发中,一般只用 privatepublic

  • 成员变量私有
  • 成员方法公开

特例:如果方法是抽取其他方法中的共性代码,这个方法一般也私有

3.2.1 private 关键字
  • private 将类的某些信息隐藏在类内部,只允许在本类中访问
  • 针对 private 修饰的成员变量,如果需要被其他类使用,提供相应的操作
    • 提供 getXxx() 方法,用于获取成员变量的值,方法用 public 修饰
    • 提供 setXxx(参数) 方法,用于设置成员变量的值,方法用 public 修饰
3.2.2 default 关键字
  • default 即默认/缺省,什么也不写,在同一包内可访问,不使用任何修饰符。
3.2.4 protected 关键字
  • protected 同一包内不同包下的子类可访问
3.2.3 public 关键字
  • public所有类可见

3.3 成员变量与局部变量

  1. 类中位置不同
    • 成员变量在类中方法外
    • 局部变量在方法内部方法声明上
  2. 内存中位置不同
    • 成员变量在堆内存中(堆中对象里)
    • 局部变量在栈内存中(栈中方法内)
  3. 生命周期不同
    • 成员变量在对象创建的时候创建,在对象被销毁的时候销毁
      引用数据类型没有被指向时空间会被释放
    • 局部变量随着方法的调用而存在,随着方法调用完毕而消失
  4. 初始化值不同
    • 成员变量有默认初始化值
    • 局部变量无默认初始化值,使用之前需要赋值
  5. 作用域不同
    • 成员变量在整个类中有效
    • 局部变量在当前方法有效

3.4 static 关键字

3.4.1 实例成员与静态成员
  1. 静态成员:
    • static 修饰的成员是属于类的,放在静态区中,叫类变量/方法静态变量/方法
    • 随着类的加载而加载,只加载一次,只存储一份,优先于对象存在
    • 所有对象共享,可以直接通过类访问
  2. 实例成员:
    • 没有 static 修饰的成员是属于对象的,叫实例变量/方法
    • 随着对象的创建而创建,每创建一个对象就存储一份
    • 必须创建具体对象才能使用,通过对象访问

3.4.2 静态变量
  • static 修饰的成员变量叫做静态变量
  • 直接用 类名.静态变量 访问
3.4.3 静态方法
  • static 修饰的成员方法叫做静态方法
  • 直接用 类名.静态方法 访问
  • 常用在测试类工具类
  • 静态方法没有 this 关键字
  • 静态方法只能访问静态变量和静态方法,不能访问非静态成员
    静态方法属于类,不属于具体对象,而 this 指代对象,非静态成员属于某个具体对象

3.5 this 关键字

成员变量和局部变量名字冲突时,遵循就近原则,使用局部变量。使用 this 修饰的变量用于指代成员变量,可以区分成员变量和局部变量的重名
方法的形参和成员变量同名时,带 this 的指成员变量,不带 this 的指形参

private int age;
public void setAge(int age) {
	this.age = age;
}
  • this 的作用:区分局部变量成员变量,用于普通的 gettersetter 方法和构造方法
  • this 的本质:代表方法调用者的地址值,即代表当前对象
    s.method(),method 由 s 调用,this 代表 s 的地址值

4. 包

4.1 包的作用

包就是文件夹,是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护

4.2 包的规则

  • 公司域名反写.技术名称,需要全部英文小写,见名知意
  • 包名每个路径必须是合法标识符,而且不能是 Java 关键字

com.baoduo.search

4.3 导包

全类名:包名.类名 com.baoduo.search.Student

//导包
import com.baoduo.search.Student;
  • 使用同一个包里的类时,不需要导包
  • 使用 java.lang 包中的类时,不需要导包
  • 其他情况都需要导包
  • 如果使用两个包中的同名类,需要使用全类名

5. 构造方法

构造方法也叫构造器、构造函数,在创建对象的时候给成员变量进行初始化的,是一种特殊的方法

5.1 构造方法的格式

  • 方法名与类名相同,大小写也要一致
  • 没有返回值类型,连 void 都没有
  • 没有具体的返回值,不能由 return 带回结果数据
修饰符 类名(参数) {
	方法体;
}

5.2 构造方法的注意事项

  1. 构造方法的定义
    • 如果没有定义构造方法,系统会给出一个默认的无参构造方法
    • 如果定义了构造方法,系统不再提供默认构造方法
  2. 构造方法的重载
    • 带参构造方法和无参构造方法,二者方法名相同,但是参数不同
  3. 推荐无论是否使用,都手写无参和带全部参数的构造方法

6. 代码块

6.1 局部代码块

提前结束变量的生命周期,节约内存(已淘汰)

{
	int a = 10;
}

6.2 构造代码块

抽取构造方法中的重复代码(不够灵活,已淘汰)
创建对象时先执行构造代码块再执行构造方法

{
	System.out.println("开始创建对象了");
}
public Student() {} 
public Student(String name) {
	this.name = name;
}

6.3 静态代码块

  • 随着类的加载而加载,并且只加载一次
  • 常用来完成对数据的初始化
static {

}

7. 对象在内存

  1. 加载 class 文件到方法区
  2. 栈内存声明局部变量
  3. 堆内存中开辟空间
  4. 默认初始化
  5. 显示初始化
  6. 构造方法初始化
  7. 堆内存中的地址值赋给局部变量

7.1 单个对象

  • 局部变量(对象名)在栈内存中,存储堆内存对象的地址
  • 堆内存中存储对象的成员变量成员方法的地址

7.2 多个对象

  • class 加载进入方法区后可直接使用,无需再次加载

7.3 两个引用指向同一对象

  • 将对象名赋给对象名实际赋的是地址值
  • 当没有指向堆内存中对象的变量时,堆内存中的对象会被释放

二、继承

1. 继承概述

1.0 引入

很多类具有很多相同的属性和方法,代码复用性差,维护麻烦
有了继承可以把相同的属性和方法抽取到父类当中,提高代码复用性

//没有继承
public class Student {
	private String name;
	private int score;
	public static void eat() {}
}
public class Teacher {
	private String name;
	public static void eat() {}
}

//有继承
public class Person {
	private String name;
	public static void eat() {}
}
public class Student extends Person{
	private int score;
}
public class Teacher extends Person{
}

1.1 什么是继承

继承:就是子类继承父类的属性行为,使得子类对象可以直接具有与父类的属性和行为。继承描述的是事物之间的所属关系,例如,兔子属于食草动物,食草动物属于动物

  • 继承者称为子类(派生类)
  • 被继承者称为父类(基类或超类)

1.2 继承的好处

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
  • 子类可以在父类基础上增加其他的功能,使子类更强大
  • 使类与类之间产生了关系

1.3 什么时候使用继承

  1. 类与类之间存在共性的内容
  2. 子类是父类中的一种

2. 继承的格式

通过 extends 关键字,可以声明一个子类继承另外一个父类

public class 子类 extends 父类 {
}
  • Java 只支持单继承,不支持多继承,但支持多层继承
  • 所有的类都直接或间接地继承于 Object

3. 继承的特点

3.1 构造方法

  • 子类不能继承父类的构造方法,但是可以通过 super() 调用
  • 子类初始化之前一定要先调用父类构造方法完成父类的初始化
  • 子类构造方法第一行语句必须为 super(),不写也默认存在
  • 如果想调用父类有参构造,必须手写 super(参数) 调用
//调用父类有参构造
public 子类(参数1,参数2) {
	super(参数1);
	this.参数2 = 参数2;
}

3.2 成员变量

  • 所有的成员变量都能继承,但 private 的无法访问,可以通过 getter/setter 方法访问
  • 就近原则:先在局部位置找,找不到再去本类成员位置找,找不到再去父类成员位置找,逐级向上
    • 什么都不加:从局部位置开始找
    • this :从本类成员位置开始找
    • super :从父类成员位置开始找

3.3 成员方法

  • 只有父类中虚方法表中的成员方法才能被子类继承
  • 虚方法表:父类把虚方法表继承给子类,子类加上自己的虚方法,一层一层向下继承
    • private
    • static
    • final
  • 就近原则:先在虚方法表中找,找不到再去父类成员位置找,逐级向上
    • this :从本类成员位置开始找
    • super :从父类成员位置开始找

3.4 方法的重写

当父类的方法不能满足子类的需求时,需要进行方法的重写

  1. 重写的方法尽量和父类保持一致
    1. 重写方法的名称、形参列表必须与父类的一致
    2. 子类重写分类方法时,访问权限必须要大于等于父类(public > protected > 空着不写)
    3. 子类重写父类方法时,返回值必须小于等于父类
  2. 只有被添加到虚方法表中的方法才能被重写
    1. 私有方法不能被重写
    2. 静态方法不能被重写

方法重写的本质:覆盖方法表中的方法

@Override 重写注解:放在重写的方法上,可以检验重写的语法是否正确,代码安全优雅,可读性强

4. this 和 super 使用总结

  • this :理解为一个局部变量,表示当前方法调用者的地址值
  • super :表示父类对象的存储空间
  1. 访问成员变量
    • this.成员变量 访问本类成员变量
    • super.成员变量 访问父类成员变量
  2. 访问成员方法
    • this.成员方法() 访问本类成员方法
    • super.成员方法() 访问父类成员方法
  3. 访问构造方法
    • this() 访问本类构造方法
    • super() 访问父类构造方法
//this访问本类构造方法设置默认值
public Student() {
	this(null, 18);
}
public Student(String name, int age) {
	this.name = name;
	this.age = age;
}

5. final 关键字

5.1 final 的作用

  1. 修饰方法:表明该方法是最终方法,不能被重写
    方法中是某种规则,只让使用,不想让变其内容
  2. 修饰类:表明该类是最终类,不能被继承
    整个类中的方法都不想被改变内容
  3. 修饰变量:叫做常量,只能被赋值一次

5.2 常量

实际开发中,常量一般作为系统的配置信息,方便维护,增强可读性
常量的命名规范:单词全部大写,单词词之间用下划线隔开

  • fianl 修饰基本变量:变量存储的数据值不能发生改变
  • final 修饰引用类型:变量存储的地址值不能发生改变,对象内部可以发生改变
    String 不能改变的原因就是使用了 private final

6. 抽象类

6.1 概述

  • 抽象方法:将共性方法抽取到父类之后,由于每个子类执行的具体内容不同,所以在父类中不能确定具体的方法体,该对象就可以定义为抽象方法
  • 抽象类:如果一个类中存在抽象方法,那么该类必须声明为抽象类

6.2 定义格式

//抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
//抽象类的定义格式
public abstract class 类名 {}

6.3 特征和意义

  • 抽象类拥有了抽象方法的能力,失去了创建对象的能力,不能实例化

假设抽象类对象调用抽象方法,抽象方法无方法体,无意义

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

没有抽象方法的抽象类目的就是不让创建该类对象

  • 可以有构造方法

创建子类对象时给属性赋值

  • 抽象类的存在是为了被子类继承

抽象类确定了成员,不确定如何实现的抽象方法交给子类去实现

  • 继承抽象类的子类:
    1. 要么重写抽象类的所有抽象方法
    2. 要么子类也声明为抽象类

    假设不重写,调用抽象方法没有意义

意义:强制让子类按照某种格式重写,在多人开发过程中提高代码规范

三、多态

0. 引入

以学校教务系统为例,学生和老师都需要登录,而登录方法只能接收一种类型的对象,需要写两个逻辑一样的方法,代码非常冗余
多态使一个方法可以接收他的所有子类,只需写一个方法

//没有多态
public static void login(Student stu) {}
public static void login(Teacher tc) {}
//有多态
public static void login(Person p) {}

1. 多态概述

定义:同类型的对象,表现出的多种形态

  • 继承关系
  • 父类引用指向子类对象「格式体现」
  • 方法重写「意义体现,不重写无意义」

2. 多态的格式和运行特点

//多态的格式
父类类型 对象名称 = 子类类型;
Anima a = new Dog();
  1. 调用成员变量时:编译看左边,运行也看左边
    • javac 编译代码时,看左边父类中有没有这个变量,如果有编译成功,没有则编译失败
    • java 运行代码时,实际获取的是左边父类中成员变量的值
  2. 调用成员方法时:编译看左边,运行看右边
    • javac 编译代码时,看左边父类中有没有这个方法,如果有编译成功,没有则编译失败
    • java 运行代码时,实际运行的是右边子类中重写的方法
      实质还是虚方法表的覆盖

3. 多态的优势和弊端

  • 优势:方法中使用父类型作为参数,可以接收所有子类对象,还可以根据传递的对象来调用不同类中的方法
  • 弊端:由于多态编译看左边父类类型,所以不能使用子类的独有功能

弊端的解决:强制类型转换

  • 转换成真正的子类类型,可以调用子类的独有功能
  • 转换类型与真实对象类型不一样会出现类型转换异常
  • 转换的时候使用 instanceof 关键字判断可避免类型转换异常
Anima a = new Dog();
Dog d = (Dog)a; // Cat c = (Cat)a; 不能乱转换,类型不同会报错

//判断 a 是否指向 Dog 类型,返回 boolean 类型
if(a instanceof Dog) {
	Dog d = (Dog)a;
} else if(a instanceof Cat) {
	Cat c = (Cat)a;
}

//jdk14 新特性,判断和强转合并成一行
if(a instanceof Dog d) {
	d.lookHome();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OG one.Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值