面向对象
1.面向对象思想
面向对象是一种以对象为中心的编程思想,能让复杂问题简单化。当需要实现某个功能时,程序员不需要了解具体的实现过 程,只需要找一个具有该功能的对象来实现
2.类和对象
类:现实世界中某一种事物具有公共特征,将这些共同特征提取出来形成的一种概念叫做类
类和对象的关系:
类是对一类事物的描述,是抽象的
对象是一类事物的实例,是具体的。
类是对象的模板,对象是类的实例
由类创建对象的过程叫做实例化
3.三大特征
3.1 封装:
3.1.1 JavaBean 规范实体类要求
- 要求所有成员变量全部 private 私有化修饰
- 必须提供所有成员变量对应的 getter and setter 方法
- 必须提供无参数构造方法
public class Student {
// 私有化成员变量 Field
private Integer id;
private String name;
private Integer age;
// boolean 类型成员变量对应的 getter 方法是 is开头 ==> isGender
// Boolean 包装类型还是 get 和 set 开头
private boolean gender; //private Boolean gender;
// 构造方法 Constructor
public Student() {}
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
// getter and setter
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
...
}
3.1.2 构造方法作用
Student stu = new Student();
/*
构造方法作用:
1. 提供数据类型,构造方法的名称要求必须是类名,JVM 会根据当前构造方法的名称 明确实例化对象的数据类型是哪一个,计算得到对应所需的内存空间
2. 用于初始化类对象成员变量数据内容
new:
1. 在内存堆区根据当前数据类型所需申请对应的内存空间
2. 申请的内存空间进行数据擦除
new + 构造方法 是实例化对象的一种形式,也是最为基础的形式。
【工厂】
*/
3.2 继承
3.2.1 作用和相关特性
继承的作用:
基本作用:子类继承父类,代码可以得到复用。(这个不是重要的作用,是基本作用。)
主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。
继承的相关特性
① B类继承A类,则称A类为超类(superclass)、父类、基类,
B类则称为子类(subclass)、派生类、扩展类。
class A{}
class B extends A{}
我们平时聊天说的比较多的是:父类和子类。
② java 中的继承只支持单继承,不支持多继承,C++中支持多继承,
这也是 java 体现简单性的一点,换句话说,java 中不允许这样写代码:
class B extends A,C{ } 这是错误的。
③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,
例如:class C extends B,class B extends A,也就是说,C 直接继承 B,
其实 C 还间接继承 A。
④ java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。
但是私有的属性无法在子类中直接访问。(父类中private修饰的不能在子类中
直接访问。可以通过间接的手段来访问。--getter setter方法访问)
⑤ java 中的类没有显示的继承任何类,则默认继承 Object类,Object类是
java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有
Object类型中所有的特征。
⑥ 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它
们之间的耦合度非常高,Account 类发生改变之后会马上影响到 CreditAccount 类
3.2.2重写与重载的区别
重写方法 Override method
子类继承父类方法,父类方法无法满足子类的特征需求,可以进行重写操作。
重写操作要求:
1. 要求方法声明完全一致,包括 返回值类型,方法名和形式参数列表
2. 权限修饰符子类权限必须大于等于父类
父类 public 子类允许 public
父类 protected 子类允许 public protected
3. Java 原作者要求,重写方法必须有 @Override 注解
@Override 是开启重写语法格式严格检查
重载方法 Reload
同一个类,或者同一个接口内
重载方法要求
1. 方法名必须一致
2. 方法形式参数列表数据类型必须不同
3. 返回值类型无限制
4. 权限修饰符无限制
/*
方法重载 Reload
*/
public static void test(int num) {
System.out.println(num);
}
public static void test(int num1, int num2) {
System.out.println(num1 + num2);
}
private static void test(float num) {
System.out.println(num);
}
多态
4.abstract 关键字
abstract 关键字首当其冲是针对于方法的约束和修饰,增强方法后期的自主性。abstract 修饰的方法相当于制定了规则,主要是方法的参数**【入参】和返回值【出参】**,后续的子类或者实现类,可以根据实际的开发情况选择合理合适的方式完成代码实现,提升代码的可复用性和特征性。
对于程序员而言,abstract 修饰方法在后续的使用中,可以降低方法冗余,提升开发效率,增强开发体验。
语法特征:
- abstract 修饰的方法有且只有方法声明,没有方法体
- abstract 修饰方法有且只能定义在 abstract 修饰的类内或者 interface 接口内
- 一个非 abstract 修饰类继承 abstract 类或者 遵从 interface 接口,要求实现在 abstract 类中或 interface 接口中有可能存在的 abstract 修饰方法。
- 阿里巴巴开发规约要求(AJCG) abstract 修饰的类推荐使用 Abstract 或者 Base 类名开头
/*
abstract 演示
*/
abstract class BaseTest {
/**
* abstract 修饰的成员方法,没有方法体
*/
public abstract void test();
}
class A extends BaseTest {
@Override
public void test() {
System.out.println("子类继承 abstract 类实现的 abstract 方法");
}
}
/**
* @author Anonymous 2023/2/24 10:24
*/
public class Demo3 {
public static void main(String[] args) {
/*
匿名对象,可以用于调用不会重复使用的方法,或者作为其他方法的参数
提高代码的执行效率和内存的使用效率
*/
new A().test();
/*
正常操作
*/
A a = new A();
a.test();
}
}
3. final 关键字
final 可以修饰类,成员变量,成员方法,局部变量
3.1 final 修饰成员变量
final 修饰的成员变量要求必须进行初始化操作
【解决方案】
- 直接初始化赋值
- 【有参】数构造方法,实例化对象过程中初始化 final 修饰成员变量
- final 修饰的成员变量一旦被赋值无法二次修改
3.2 final 修饰成员方法
final 修饰成员方法不允许被子类重写
一般用于框架,业务核心代码内容修饰,不允许修改/重写代码内容
class C {
public final void test() {
System.out.println("C 类 test 成员方法");
}
}
class D extends C {
/*
'test()' cannot override 'test()' in 'com.qfedu.b_final.C'; overridden method is final
final 修饰的成员方法,子类不允许重写。
一般用于框架,业务核心代码内容修饰,不允许修改/重写代码内容
@Override
public void test() {
System.out.println("D 类继承 C 类重写 test 方法");
}
*/
}
public class Demo2 {
public static void main(String[] args) {
new D().test();
}
}
3.3 final 修饰类
final 修饰类没有子类
在开发中基本类型不允许继承重新定义/制定规则。
例如:
Java 中是 String 类,基本数据类型的包装类 Integer Float Double
final class E {
}
/*
Cannot inherit from final 'com.qfedu.b_final.E'
不能继承一个 final 修饰类
final 修饰类没有子类
在开发中基本类型不允许继承重新定义/制定规则。
例如:
Java 中是 String 类,基本数据类型的包装类 Integer Float Double
*/
//class F extends E {
//
//}
public class Demo3 {
}
3.4 final 修饰局部变量
final 修饰的局部变量首次赋值允许,后期不允许二次赋值
public class Demo4 {
public static void main(String[] args) {
final int num ;
/*
final 修饰的局部变量,【首次】赋值没有任何的问题
1. 定义时候初始化
2. 后期代码赋值
*/
num = 10;
System.out.println(num);
/*
Cannot assign a value to final variable 'num'
不能重新赋值一个 final 修饰的局部变量(local variable) num
*/
num = 20;
}
}
3.5 final 小面试题
class Person {
public String name;
public Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
/**
* @author Anonymous 2023/2/24 10:58
*/
public class Demo5 {
public static void main(String[] args) {
final Person person = new Person("张三", 66);
System.out.println(person.name);
System.out.println(person.age);
/*
以下代码错误的是:
person.age = 20;
person.name = "李四";
person = new Person();
答案
全错
1 2 错 3 对
1 2 对 3 错 【正确答案】
【注意】
final 修饰引用数据类型变量,指向不可变,指向数据空间内容可变
从底层逻辑分析,final 修饰的为引用数据类型变量,当前变量存储数据不可变,变量存储
内容是一个【地址】,表示地址无法修改,但是对应地址指向的数据空间内容,没有被 final
修饰,可以修改数据内容。
*/
}
}
3.6 final 使用总结
1. final 修饰类一般是架构中的底层核心代码,基本数据类型
2. final 修饰成员变量一般用于常量使用,并且在开发中大量存在,例如: 订单状态,用户状态,设备状态
3. final 修饰局部变量一般用于常量数据使用,同时是为了避免【生命周期问题】。
4. final 修饰成员方法是核心业务逻辑方法
4. static【重点 难点】
4.1 类加载【重点】
程序所需资源加载过程
游戏加载:
在游戏开始之前,需要加载游戏的相关资源(地图,角色,角色皮肤,角色特效,网络情况...)。在游戏开始之前完成的工作。
类加载同理
JVM 会根据当前程序运行所需,加载所有的类文件(.class 文件),程序准备阶段,并且在准备阶段过程中,会执行加载【static】修饰相关内容。
【核心】
三个重点:静态成员变量,静态成员方法,静态代码块
一个原则: 类加载过程中 static 修饰内容完成准备工作
【没有对象】
4.2 static 修饰静态变量
语法特征:
1. static 修饰的静态成员变量在内存的【数据区】
2. static 修饰的静态成员变量在整个代码的运行周期中有且只有一个。
3. static 修饰的静态成员变量在类文件加载阶段,需要准备就绪,已具备数据提供能力和数据存储能力。
4. static 修饰的静态成员变量别名【类变量】,语法建议直接通过类名操作
例如:
Integer.MAX_VALUE ==> public static final int MAX_VALUE = 0x7FFFFFFF;
5. 【使用建议】
静态成员变量最好不要和类对象有相关性。
静态成员变量生命周期从类加载开始到程序结果
类对象是从实例化对象开始,到 JVM GC 回收结束
可以实例化对象相当于程序开始运行,晚于静态成员变量加载
GC 收回是在程序退出之前,完成内存收回工作之后,静态成员变量销毁。
package com.qfedu.c_static;
class A {
/*
static 修饰静态成员变量
*/
public static int num = 10;
}
/**
* @author Anonymous 2023/2/24 11:49
*/
public class Demo1 {
public static void main(String[] args) {
// 通过类名可以直接调用当前静态成员变量
System.out.println(A.num);
// 当前情况下,匿名对象生命周期有且只在当前行,超出对象销毁
new A();
/*
Static member 'com.qfedu.c_static.A.num' accessed via instance reference
【警告】
static 修饰的静态内存,通过实例化对象引用,IDE(Eclipse IDEA MyEclipse) 工具不建议
*/
System.out.println(new A().num);
// 对象已销毁,但是静态成员变量依然可以通过类名直接调用。
System.out.println(A.num);
}
}
4.3 static 修饰静态成员方法
语法特征:
1. 静态成员方法使用 static 修饰
2. static 修饰的静态成员方法不允许使用类内的【非静态成员】
static 修饰的静态成员方法在类文件加载阶段已具备执行能力,方法内有非静态相关内容,无法执行,因为
非静态成员需要实例化对象调用操作,加载过程中没有相关的对象存在。【没有对象】
3. static 修饰的静态成员方法可以直接使用类内的其他静态资源。
4. 静态成员方法常用于【工具类】方法封装
Arrays
静态成员方法是可以通过类名直接调用,可以摆脱类对象的限制,执行效率较高。无需考虑对象实例化过程,以及
销毁对象过程的时间和内存空间的占用浪费。
5. 静态成员方法推荐使用类名直接调用,也可以称之为【类方法】
package com.qfedu.c_static;
import java.util.Arrays;
class MyArrayUtils {
int num = 10;
static int test = 10;
public void test() {
System.out.println("测试");
}
/**
* 静态成员方法,降序 int 数据类型选择排序
*
* @param arr int 类型数组
*/
public static void selectSortDesc(int[] arr) {
/*
Non-static field 'num' cannot be referenced from a static context
static 修饰静态内容不可以引用非静态成员变量
*/
//System.out.println(num);
// 难兄难弟,互不嫌弃
System.out.println(test);
/*
Non-static method 'test()' cannot be referenced from a static context
static 修饰静态内容不可以引用非静态成员方法
*/
// test();
if (null == arr || 0 == arr.length) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int index = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[index] < arr[j]) {
index = j;
}
}
if (index != i) {
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
showArray(arr);
}
public static void showArray(int[] arr) {
System.out.println(Arrays.toString(arr));
}
}
/**
* @author Anonymous 2023/2/24 14:50
*/
public class Demo2 {
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
// 直接通过类名调用静态成员方法
MyArrayUtils.selectSortDesc(arr);
MyArrayUtils.showArray(arr);
}
}
4.4 static 修饰静态代码块
4.4.1 补充知识点
代码块有三种形式
- 构造代码块【不多见】
- 局部代码块【已淘汰】
- 静态代码块【目前还有很大作用】
构造代码块
package com.qfedu.c_static;
/**
* @author Anonymous 2023/2/24 15:13
*/
public class Demo3 {
/*
构造代码块
Java 编译器在编译代码的过程中,会将构造代码块内容放入到每一个构造方法的【第一行】
first statement
*/
{
System.out.println("构造代码块");
}
public Demo3() {
System.out.println("无参数构造方法");
}
public Demo3(int num) {
System.out.println(" int 参数构造方法");
}
public Demo3(String message) {
System.out.println("String 参数构造方法");
}
public static void main(String[] args) {
new Demo3();
System.out.println();
new Demo3(10);
System.out.println();
new Demo3("字符串");
System.out.println();
}
}
局部代码块
int num = 10;
/*
局部代码块,利用 Java 局部变量特征,压缩局部变量生存空间。
num2 作用范围和生命周期都在 大括号范围以内。
提高内存的复用度,加快 GC 垃圾回收。
*/
{
int num2 = 20;
}
System.out.println(num);
System.out.println(num2);
4.4.2 静态代码块特征
1. 静态代码块在类文件加载阶段一定执行!!!并且有且只执行一次。
2. 静态代码块可以调用类内的其他静态资源
3. 静态代码块不可以调用类内的非静态资源
4. 静态代码块一般用于程序初始化操作,预处理操作,项目运行前准备工作。
案例:
配置资源读取
配置文件读取
相关资源加载
/**
* 静态代码块
*
* @author Anonymous 2023/2/24 15:41
*/
public class Demo4 {
public int num = 10;
public static String msg = "测试";
// 静态代码块
static {
/*
Non-static field 'num' cannot be referenced from a static context
不可以在一个静态区域中引用非静态成员变量
System.out.println(num);
*/
System.out.println(msg);
staticMethod();
/*
Non-static method 'test()' cannot be referenced from a static context
不可以在一个静态区域中引用非静态成员方法
test();
*/
System.out.println("静态代码块资源");
}
public void test() {
System.out.println("非静态成员方法");
}
public static void staticMethod() {
System.out.println("静态成员方法");
}
public static void main(String[] args) {
Class cls = Demo4.class;
new Demo4();
new Demo4();
new Demo4();
new Demo4();
new Demo4();
new Demo4();
new Demo4();
new Demo4();
new Demo4();
}
}