第四章 面向对象
4.1 方法
- 方法:就是一种功能的体现。方法是可以复用的
- 格式:
方法的修饰符 返回类型 方法名称(参数){
方法体
}
- 方法的修饰符:public、private、protected、abstract、static、final...
- 返回类型:如果方法没有返回值那么使用void修饰,如果有返回类型那么方法的返回值必须是这种类型。
- 方法名称:符合标识符即可
- 方法的参数:可以没有或者多个,多个使用逗号分隔符
- 案例:
public class Demo {
public static void main(String[] args){
method();
}
//自定义方法
public static void method(){
System.out.println("这是一个没有返回和参数的方法");
}
}
- 含一个参数的方法
public class Demo {
public static void main(String[] args){
method(1);
}
public static void method(int key){
switch(key){
case 1:
System.out.println("执行了第一个语句");
break;
case 2:
System.out.println("执行了第二个语句");
break;
default:
System.out.println("执行了其他语句");
break;
}
}
}
- 含两个参数的方法
public class Demo {
public static void main(String[] args){
method(1,2);
}
//自定义方法
public static void method(int m,int n){
System.out.println(m+n);
}
}
- 含有返回值得方法
public class Demo {
public static void main(String[] args){
int sum = method(1,2);
System.out.println(sum);
}
//自定义方法
public static int method(int m,int n){
return m+n;
}
}
4.2 方法的重载
4.2.1 重载的条件
- 在同一个类中
- 方法的名称相同的
- 方法的参数的类型、个数、顺序有一个不同即可
- 与方法的返回类型没有关系
4.2.2 不使用重载
public class Demo {
public static void main(String[] args){
int methodInt = methodInt(1,2);
}
//因为没有重载,所以增加了记忆方法的困难
public static int methodInt(int m,int n) {
return m + n;
}
public static double methodDouble(double m,int n){
return m + n;
}
public static long methodLong(long m,int n){
return m+n;
}
}
4.2.3 使用重载:减少记忆
public class Demo {
public static void main(String[] args){
sum(1,2);
}
public static int sum(int m,int n) {
return m + n;
}
public static double sum(double m,int n){
return m + n;
}
public static long sum(long m,int n){
return m+n;
}
}
4.2.4 重载的条件
- 参数的类型不能相同

- 参数的个数不能相同

- 参数的类型顺序不能相同

4.3 递归
4.3.1 案例:求10以内的和
public class Demo {
public static void main(String[] args){
int sum = 0;
for (int i = 0; i < 6; i++) {
sum += i;
}
System.out.println(sum);
}
}
4.3.2 案例:使用递归
- 自身的调用;就是自己调用自己
public class Demo {
public static void main(String[] args){
int sum = rec(5);
System.out.println(sum);
}
//递归
public static int rec(int i){
if(i==1)
return 1;
return i+rec(i-1);
}
}
- 详解
public class Demo {
public static void main(String[] args){
int sum = rec(5);
System.out.println(sum);
}
//递归
/**
* 返回:5+4+3+2+1
5+rec(5-1)
4+rec(4-1)
3+rec(3-1)
2+rec(2-1)
1
*/
public static int rec(int i){
if(i==1)
return 1;
return i+rec(i-1);
}
}
- n的阶层
public static int rec(int n){
if(n==1)
return 1;
return n*rec(n-1);
}
4.4 使用方法完成项目
4.4.1 第一版
import java.util.Scanner;
public class Demo {
public static void main(String[] args){
//控制台对象
Scanner scanner = new Scanner(System.in);
//跳出循环的标识
boolean flag = true;
//提示信息
System.out.println("1添加;2显示;3删除;4更新;5退出;");
//循环
do {
int key = scanner.nextInt();
switch (key){
case 1:
System.out.println("请输入一个数字");
save(scanner.nextInt());
break;
case 2:
findAll();
break;
case 3:
System.out.println("请输入一个需要删除的数字");
delete(scanner.nextInt());
break;
case 4:
System.out.println("请输入需要更新的数字");
int i = scanner.nextInt();
System.out.println("请输入需要更新的新数字");
int n = scanner.nextInt();
update(i,n);
break;
case 5:
flag = false;
break;
}
}while(flag);
}
//创建数组
static int[] array = new int[3];
//获取数据的下标
public static int findByIndex(int element){
//通过判断element的值决定查找空位置还是对应元素的下标
for (int index = 0; index < array.length; index++) {
if (element == 0) {//获取空位置
if (array[index] == 0) {
return index;
}
} else {//查询对应元素的下标
if (array[index] == element) {
return index;
}
}
}
return -1;
}
//添加数据
public static void save(int el){
//获取一个保存元素的空位置
int index = findByIndex(0);
//判断下标
if (index!=-1){
array[index] = el;
System.out.println("操作成功");
}else{
System.out.println("full");
}
}
//显示数据
public static void findAll(){
for (int index = 0; index < array.length; index++) {
int i = array[index];
if (i!=0) {
System.out.println(i);
}
}
}
//删除数据
public static void delete(int el){
//根据元素获取下标
int index = findByIndex(el);
//判断
if (index!=-1){
array[index] = 0;
System.out.println("操作成功");
}else {
System.out.println("查无此数据,请重新输入");
}
}
//更新数据
public static void update(int el,int target){
//根据元组获取下标
int index = findByIndex(el);
//判断
if (index!=-1) {
array[index] = target;
System.out.println("操作成功");
}else{
System.out.println("查无此数据请重新输入");
}
}
}
4.4.2 第二版
import java.util.Scanner;
public class Demo {
public static void main(String[] args){
//控制台对象
Scanner scanner = new Scanner(System.in);
//跳出循环的标识
boolean flag = true;
//提示信息
System.out.println("1添加;2显示;3删除;4更新;5退出;");
//循环
do {
int key = scanner.nextInt();
switch (key){
case 1:
save(scanner);
break;
case 2:
findAll();
break;
case 3:
delete(scanner);
break;
case 4:
update(scanner);
break;
case 5:
flag = false;
break;
}
}while(flag);
}
//创建数组
static int[] array = new int[3];
//获取数据的下标
public static int findByIndex(int element){
//通过判断element的值决定查找空位置还是对应元素的下标
for (int index = 0; index < array.length; index++) {
if (element == 0) {//获取空位置
if (array[index] == 0) {
return index;
}
} else {//查询对应元素的下标
if (array[index] == element) {
return index;
}
}
}
return -1;
}
//添加数据
public static void save(Scanner scanner){
//获取一个保存元素的空位置
int index = findByIndex(0);
//判断下标
if (index!=-1){
System.out.println("请输入一个数字");
array[index] = scanner.nextInt();
System.out.println("操作成功");
}else{
System.out.println("full");
}
}
//显示数据
public static void findAll(){
for (int index = 0; index < array.length; index++) {
int i = array[index];
if (i!=0) {
System.out.println(i);
}
}
}
//删除数据
public static void delete(Scanner scanner){
System.out.println("请输入一个需要删除的数字");
//根据元素获取下标
int index = findByIndex(scanner.nextInt());
//判断
if (index!=-1){
array[index] = 0;
System.out.println("操作成功");
}else {
System.out.println("查无此数据,请重新输入");
}
}
//更新数据
public static void update(Scanner scanner){
System.out.println("请输入需要更新的数字");
//根据元组获取下标
int index = findByIndex(scanner.nextInt());
//判断
if (index!=-1) {
System.out.println("请输入需要更新的新数字");
array[index] = scanner.nextInt();
System.out.println("操作成功");
}else{
System.out.println("查无此数据请重新输入");
}
}
}
4.5 面向过程区别面向对象
- c语言就是面向过程语言
- 面向过程的特点:必须了解整个过程,每个步骤的因果关系
- 面向对象:将现实世界分为不同的对象,把各个对象协作起来
- 就是面对一个真实存在的
- 面向对象的特性:实质就是面向抽象
- 抽象:具有这一类事务的特征和特性
- 面向对象符合人的思维
4.5.1 面向对象的特性
- 封装
- 继承
- 多态
4.5.2 类与对象的概念
- 类:对具有共性事物的抽象描述
- 例如:学生类=学号、姓名、性别、年龄、班级
- 对象(实例):就是对类的具体化、实例化
- 例如:学生对象:1001、小灰灰、男、6、3班

- 以上的类图中都是属性,进一步细化,添加类中的方法

- 结论:属性源自于一种状态,方法源自于一个功能(动作)
- 类=属性+方法
- 类=成员+功能
4.5.3 类的定义
格式:
类的修饰符 class 类名称 extends 父类名称 implements 接口名称{
类体:属性+方法
}
- 案例:创建类
public class Student {
//学号
int id;
//姓名
String name;
//性别
String sex;
//地址
String address;
//年龄
int age;
}
- 以上都是属性,也成为成员变量
- 成员变量会默认初始化
- 注意:局部变量不能默认初始化,必须初始化
4.5.4 对象的创建和使用
- 使用new关键字创建对象,才可以使用
public class TestDemo {
public static void main(String[] args) {
//创建对象:把堆内存中这个对象的内存地址赋值给参数student了
Student student = new Student();
System.out.println("内存地址:"+student);
}
}
- 属性默认值

- 创建流程图

- 具体的默认值
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
boolean | false |
char | '' |
引用类型 | null |

4.5.4.1 可以对成员变量进行赋值
- 就是把默认值替换了

- 一个类可以创建N个对象,并且,每个对象的成员值属于当前对象;
- 只有通过对象才可以修改或者访问这个对象的成员(属性);
4.6 封装性
- 属性私有化,细节被隐藏掉,安全性提高了。
- private关键字作用就是把属性保护起来了。
- 在本类中可以访问
- 就只能自己使用
- 就是为了安全,外界无法访问
4.6.1 添加一个方法完成对成员(属性)的操作
- 添加方法(功能)的目的,就是在使用的时候可以添加各种限制(条件,添加判断);
- 方法就是对成员操作的
- 案例
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
//通过方法把外面的数据添加到对象中的成员上
student.setAge(20);
//通过方法把成员的数据获取
int age = student.getAge();
System.out.println(age);
}
}
class Student {
//学号
private int id;
//姓名
private String name;
//性别
private String sex;
//地址
private String address;
//年龄
private int age;
//添加年龄
public void setAge(int nl) {
if (nl > 0 & nl < 300) {
age = nl;
} else {
System.out.println("请添加范围内的数字");
}
}
//获取年龄
public int getAge() {
return age;
}
}
通过上面的案例,避免了直接操作对象的属性,这就是封装;让外界对类内部知道的越少越好;
结论:封装属性;暴露方法
4.7 构造方法(构造器、构造函数)
- 构造方法就是创建对象的
4.7.1 格式:
- 构造方法的修饰符:private public protected
- 构造方法和普通方法一样都可以进行重载
- 构造方法特点:
- 与类同名
- 构造方法不能有任何返回类型,即没有返回值,关键字void也不能加;如果添加了void那么就不是构造方法了,而是普通方法。
- 任何类都有构造方法,如果没有显示的编写,在创建对象时,系统会分配一个默认的无参数构造方法;如果显示的定义了构造方法,系统就不会创建默认的无参数构造方法了。
- 4.7.2 案例:无参构造方法
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student {
//系统默认分配了一个无参数的构造方法
}
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student {
//无参数构造方法
public Student(){
System.out.println("无参数构造方法");
}
}
- 添加void那么就变为普通方法
class Student {
//创建了一个普通方法
public void Student(){
System.out.println("创建了一个普通方法");
}
}
4.7.3 显示编写构造方法
- 创建对象时,系统不会在分配默认的构造方法
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student {
//构造方法重载
//无参数构造方法
public Student(){
System.out.println("无参数构造方法");
}
//有参数构造方法
public Student(int i){
System.out.println("有参数构造方法");
}
}
- 初始化
public class TestDemo {
public static void main(String[] args) {
Student student = new Student(1001, "张三", "男");
}
}

4.8 对象和引用
4.8.1 java内存的主要划分

4.8.2 内存的表示
- 主方法main压入栈,就创建一个student对象
public class TestDemo {
public static void main(String[] args) {
Student student = new Student(1001, "张三", "男");
}
}

- 对student这个对象中的成员赋值
public class TestDemo {
public static void main(String[] args) {
Student student = new Student(1001, "张三", "男");
}
}
class Student {
//学号
private int id;
//姓名
private String name;
//性别
private String sex;
public Student(int i,String username,String xb){
id = i;
name = username;
sex = xb;
}
}

4.8.3 当不使用new关键字时,出现问题

4.9 this关键字
- this表示当前对象
- 如果堆中有n个对象,会有n个this指向对应的对象
4.9.1 局部变量
- 当局部变量与成员变量名称相同时,可以使用this加以区分,因为就近原则
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
}
}
class Student {
//姓名
private String name;
public void Student(String name){
//就把name参数的数值赋值给当前对象的成员变量了
this.name = name;
}
}
- 自动生成setter和getter方法



4.9.2 可以调用其他的构造方法
- 注意:调用构造方法时必须在首行
4.9.2.1 这样调用构造方法是错误的,因为,创建了垃圾对象
class Student {
public Student() {
System.out.println("无参数");
//这样调用构造方法是错误的,因为,创建了垃圾对象
new Student(1);
}
public Student(int i) {
System.out.println("1参数");
}
public Student(int i, int n) {
System.out.println("2参数");
}
}
4.9.2.2 这样调用是正确的
public class Demo{
public static void main(String[] args){
Student student = new Student();
}
}
class Student {
public Student() {
this(10);
System.out.println("无参数");
}
public Student(int i) {
this(1,2);
System.out.println("1参数");
}
public Student(int i, int n) {
System.out.println("2参数");
}
}
4.9.3 this可以调用成员方法
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
class Student {
public void show(){
System.out.println("show-method");
this.save();
}
public void save(){
System.out.println("show-method");
}
}
- 调用成员方法时也可以不使用this关键字(推荐)
class Student {
public void show(){
System.out.println("show-method");
save();
}
public void save(){
System.out.println("show-method");
}
}
4.9.4 javaBean
- bean就是一种特殊的类
- 含有setter和getter方法
- 可以看做数据的载体
class Student {
private int id;
private String name;
private String sex;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5.0 项目-优化
5.0.1 需求:
- 对学生信息完成CRUD
- 使用数组存储学生信息(学生对象)
5.0.2 技术
- javase+数组+循环
- 技术分析:
- 创建一个可以存储学生类型的数组
- 创建保存学生的方法:save
- 显示学生的方法:findAll
- 删除学生的的方法:delete
- 更新学生的方法:
- 查询到需要更新的学生:findByid
- 更新学生的信息:update只更新姓名
- 根据学生的学号查找在数组中这个学生对应的下标:findByindex
5.0.3 编码实现
import java.util.Scanner;
public class TestDemo {
//创建一个学生数组
static Student[] array = new Student[3];
/***
* TODO 按照学号查找对应学生的下标,如果sid=0表示需要找空位置完成添加
* @param sid 表示学号
* @return 下标
*/
public static int findByIndex(int sid){
//循环
for (int index = 0; index < array.length; index++) {
//根据下标获取对应的学生对象
Student student = array[index];
//判断是否存在
if (sid==0){
if (student==null){
return index;
}
}else{
if (student!=null && student.getId()==sid){
return index;
}
}
}
return -1;
}
/***
* TODO 保存学生信息
* @param student 学生对象(地址)
*/
public static void save(Student student){
//获取一个空位置
if (student!=null){
int index = findByIndex(0);
if (index!=-1){
array[index] = student;
System.out.println("操作成功");
}else{
System.out.println("满了");
}
}else{
System.out.println("请添加学生信息");
}
}
/**
* TODO 显示学生信息
*/
public static void findAll(){
//遍历数组中学生的信息
for (int index = 0; index < array.length; index++) {
//获取学生对象
Student student = array[index];
if (student!=null){
System.out.println("学号:"+student.getId()+"\t"+
"姓名:"+student.getName()+"\t"+
"性别:"+student.getSex()+"\t"+
"年龄:"+student.getAge()
);
}
}
}
/***
* TODO 删除学生
* @param sid 学号
*/
public static void delete(int sid){
//获取学生在数组中的位置
int index = findByIndex(sid);
//判断
if (index!=-1){
array[index] = null;
System.out.println("操作成功");
}else{
System.out.println("查无此学生,请重新输入学号");
}
}
public static void update(int sid,String name){
//按照学号查找对应的下标
int index = findByIndex(sid);
//判断
if (index!=-1) {
//获取这个学生
Student student = array[index];
//修改姓名
student.setName(name);
System.out.println("操作成功");
}else{
System.out.println("查无此学生,请重新输入学号");
}
}
/***
* TODO 测试方法
* @param args
*/
public static void main(String[] args) {
//控制台对象
Scanner scanner = new Scanner(System.in);
//跳出循环体
boolean flag = true;
//提示
System.out.println("1添加;2显示;3删除;4更新;5退出");
//循环
do {
//key
int key = scanner.nextInt();
switch (key){
case 1://调用方法
Student student = new Student();
//提示信息
System.out.println("请输入学号");
student.setId(scanner.nextInt());
System.out.println("请输入姓名");
student.setName(scanner.next());
System.out.println("请输入性别");
student.setSex(scanner.next());
System.out.println("请输入年龄");
student.setAge(scanner.nextInt());
//保存
save(student);
break;
case 2://调用方法
findAll();
break;
case 3:
System.out.println("请输入学号");
delete(scanner.nextInt());
break;
case 4:
System.out.println("请输入学号");
int sid = scanner.nextInt();
System.out.println("请输入姓名");
String name = scanner.next();
//更新
update(sid,name);
break;
case 5:
flag = false;
break;
}
}while(flag);
}
}
class Student {
private int id; //学号
private String name; //学生姓名
private String sex; //学生性别
private int age; //学生年龄
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5.0.4 编码实现-优化版
5.0.4.1 扩展
- 返回字符串
import java.util.UUID;
public class Demo1 {
public static void main(String[] args){
//不重复
String str = UUID.randomUUID().toString();
System.out.println(str);
}
}
- 返回long
import java.util.UUID;
import java.util.zip.CRC32;
public class Demo1 {
public static void main(String[] args){
//不重复
String str = UUID.randomUUID().toString();
CRC32 crc32 = new CRC32();
crc32.update(str.getBytes());
//获取
long value = crc32.getValue();
System.out.println(value);
}
}
- 实现
import java.util.Scanner;
import java.util.UUID;
import java.util.zip.CRC32;
public class TestDemo {
//创建一个学生数组
static Student[] array = new Student[3];
/***
* TODO 按照学号查找对应学生的下标,如果sid=0表示需要找空位置完成添加
* @param sid 表示学号
* @return 下标
*/
public static int findByIndex(int sid){
//循环
for (int index = 0; index < array.length; index++) {
//根据下标获取对应的学生对象
Student student = array[index];
//判断是否存在
if (sid==0){
if (student==null){
return index;
}
}else{
if (student!=null && student.getId()==sid){
return index;
}
}
}
return -1;
}
/***
* TODO 保存学生信息
* @param student 学生对象(地址)
*/
public static void save(Student student){
//获取一个空位置
if (student!=null){
int index = findByIndex(0);
if (index!=-1){
array[index] = student;
System.out.println("操作成功");
}else{
System.out.println("满了");
}
}else{
System.out.println("请添加学生信息");
}
}
/**
* TODO 显示学生信息
*/
public static void findAll(){
//遍历数组中学生的信息
for (int index = 0; index < array.length; index++) {
//获取学生对象
Student student = array[index];
if (student!=null){
System.out.println("学号:"+student.getId()+"\t"+
"姓名:"+student.getName()+"\t"+
"性别:"+student.getSex()+"\t"+
"年龄:"+student.getAge()
);
}
}
}
/***
* TODO 删除学生
* @param sid 学号
*/
public static void delete(int sid){
//获取学生在数组中的位置
int index = findByIndex(sid);
//判断
if (index!=-1){
array[index] = null;
System.out.println("操作成功");
}else{
System.out.println("查无此学生,请重新输入学号");
}
}
public static void update(int sid,String name){
//按照学号查找对应的下标
int index = findByIndex(sid);
//判断
if (index!=-1) {
//获取这个学生
Student student = array[index];
//修改姓名
student.setName(name);
System.out.println("操作成功");
}else{
System.out.println("查无此学生,请重新输入学号");
}
}
public static long getValue(){
//不重复
String str = UUID.randomUUID().toString();
CRC32 crc32 = new CRC32();
crc32.update(str.getBytes());
//获取
long value = crc32.getValue();
return value;
}
/***
* TODO 测试方法
* @param args
*/
public static void main(String[] args) {
//控制台对象
Scanner scanner = new Scanner(System.in);
//跳出循环体
boolean flag = true;
//提示
System.out.println("1添加;2显示;3删除;4更新;5退出");
//循环
do {
//key
int key = scanner.nextInt();
switch (key){
case 1://调用方法
Student student = new Student();
//提示信息
student.setId((int)getValue());
System.out.println("请输入姓名");
student.setName(scanner.next());
System.out.println("请输入性别");
student.setSex(scanner.next());
System.out.println("请输入年龄");
student.setAge(scanner.nextInt());
//保存
save(student);
break;
case 2://调用方法
findAll();
break;
case 3:
System.out.println("请输入学号");
delete(scanner.nextInt());
break;
case 4:
System.out.println("请输入学号");
int sid = scanner.nextInt();
System.out.println("请输入姓名");
String name = scanner.next();
//更新
update(sid,name);
break;
case 5:
flag = false;
break;
}
}while(flag);
}
}
class Student {
private int id; //学号
private String name; //学生姓名
private String sex; //学生性别
private int age; //学生年龄
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5.1 静态关键字static
5.1.1 修饰
- 可以修饰变量、方法、代码块
- 特点:
- 使用static修饰的类可以直接加载执行,所以,static修饰的变量也成为类变量
- static修饰的代码块称为静态代码块,JVM加载类的时候,会执行静态代码块中的内容

5.1.2 采用静态变量实现累加器
- 没有采用静态变量,而是成员变量,所以不会实现累加器的效果
- 因为,成员变量属于当前对象,不会被共享
public class Demo1 {
public static void main(String[] args){
//创建对象
Student student1 = new Student();
Student student2 = new Student();
//获取成员变量
System.out.println(student1.getCount());
System.out.println(student2.getCount());
}
}
class Student{
//成员变量(属性)
private int count;
//构造方法
public Student(){
count++;
}
//获取属性
public int getCount(){
return count;
}
}

- 采用静态变量完成累加器的功能
public class Demo1 {
public static void main(String[] args){
//创建对象
Student student1 = new Student();
Student student2 = new Student();
//获取静态变量
System.out.println(student1.getCount());
System.out.println(student2.getCount());
}
}
class Student{
//静态变量
private static int count;
//构造方法
public Student(){
count++;
}
//获取属性
public int getCount(){
return count;
}
}

- static声明的变量通过该类new出的对象,都可以"共享",都可以通过对象访问
- 类也可以直接访问,所以,我们称为"类变量"
- 类直接调用
public class Demo1 {
public static void main(String[] args){
System.out.println(Student.count);
}
}
class Student{
//静态变量
public static int count;
}
5.1.3 笔试题
5.1.3.1 静态方法中是否可以访问实例变量、实例方法或this关键字?
答:不能,因为,静态修饰的在类加载器就初始了,而对象这个时候还不存在

- 我们可以改善一下代码
- 把方法修改为static修饰

- 使用对象调用

- 不能在静态中直接使用this

5.1.3.2 静态方法的初始化顺序
- 就是按照static修改的顺序执行
public class Demo1 {
static{
System.out.println(1);
}
static{
System.out.println(2);
}
static{
System.out.println(3);
}
public static void main(String[] args){
System.out.println(4);
}
}
- 输出为1234,因为,主方法需要压栈后才执行,静态块可以直接执行
public class Demo1 {
public static void main(String[] args){
System.out.println(4);
}
static{
System.out.println(1);
}
static{
System.out.println(2);
}
static{
System.out.println(3);
}
}
5.1.3.3 执行的顺序
- 1静态块 2语句块 3构造方法
public class Demo1 {
public static void main(String[] args){
//创建对象时,1静态块 2语句块 3构造方法
Student student = new Student();
}
}
class Student{
//静态变量
public Student(){
System.out.println("构造方法");
}
//静态块
static{
System.out.println("静态块");
}
//语句块
{
System.out.println("语句块");
}
}
5.2 设计模式
- 设计模式:就是一种可以反复利用的解决方案;
- 设计模式是1995年由GOF4人组提出
- 设计模式:23种
- 从结构上分为:
- 创建型
- 结构型
- 行为型
5.2.1 单例模式
- 单例模式:就是单实例(一个对象)的一种模式,也称为"反模式"
- 优缺点:
- 优点:堆中只创建一个对象,使用堆内存少,成本低
- 缺点:因为对象是被共享的,所以,有安全问题。
- 创建单例模式:
- 构造方法私有化
- 创建一个私有的、静态的、本类型对象
- 创建一个公有的、静态的、返回本类型的方法
- 自律模式
public class Demo1 {
public static void main(String[] args){
Student student1 = Student.getInstance();
Student student2 = Student.getInstance();
//比较两个对象的内存地址
System.out.println(student2 == student1);
}
}
//单例模式:饿汉式:推荐使用
class Student{
//创建一个私有的、静态的对象
private static Student student = new Student();
//私有化
private Student(){}
//创建一个公有的、静态的、返回本类型
public static Student getInstance(){
return student;
}
//成员方法
public void method(){
System.out.println("成员方法");
}
}
- 单例模式
- 饿汉式:推荐使用
- 懒汉式:加锁--变为自旋锁
- 可以调用成员方法
public class Demo1 {
public static void main(String[] args){
Student student1 = Student.getInstance();
//可以通过单例对象调用成员方法了
student1.method();
}
}
//单例模式:饿汉式
class Student{
//创建一个私有的、静态的对象
private static Student student = new Student();
//私有化
private Student(){}
//创建一个公有的、静态的、返回本类型
public static Student getInstance(){
return student;
}
//成员方法
public void method(){
System.out.println("成员方法");
}
}
5.3 优化项目
5.3.1 分类完成功能
- 使用数组模拟数据库:可以创建一个单独的类
- 实现功能CRUD:独立的类完成
- 前端:独立类完成
5.3.2 开发
开发流程图:开发就是这个过程的倒过程

5.3.2.1 创建实体类:即javaBean
public class Student {
private int id;
private String username;
private String sex;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5.3.2.2 创建数据库类:就是管理数组的
public class DataBaseYgm {
//构造方法私有化
private DataBaseYgm(){}
//创建对象
private static DataBaseYgm dataBaseYgm = new DataBaseYgm();
//创建方法
public static DataBaseYgm getInstance(){
return dataBaseYgm;
}
//创建数据库
private Student[] students = new Student[3];
//返回数组的方法:成员方法
public Student[] getStudents(){
return students;
}
}
5.3.2.3 业务类service
import java.util.Scanner;
//业务类
public class StudentService {
//创建控制台
Scanner scanner = new Scanner(System.in);
//依赖数据库
private Student[] array = DataBaseYgm.getInstance().getStudents();
/**
* 获取对应下标
* @param sid 学号
* @return 对应的下标
*/
public int findByIndex(int sid){
//获取学生对象
for (int index = 0; index<array.length; index++){
Student student = array[index];
//判断
if (sid == 0){
if (student == null) return index;
}else{
if (student!=null && student.getId() == sid ){
return index;
}
}
}
return -1;
}
//保存学生
public void save(Student student){
if (student!=null){
//获取一个空位置
int index = findByIndex(0);
if (index!=-1){
array[index] = student;
}else{
System.out.println("满了");
}
}else{
System.out.println("请输入学生信息");
}
}
/**
* TODO 查询所有学生
*/
//查询学生
public Student findUid(int id) {
int index = findByIndex(id);
//判断
if (index != -1) {
return array[index];
} else {
return null;
}
}
/**
* TODO 删除学生
* @param sid 学号
*/
//删除学生
public void delete(int sid){
//获取学生在数组中的位置
int index = findByIndex(sid);
//判断
if (index != -1){
array[index] = null;
System.out.println("操作成功");
}else{
System.out.println("无此学生信息,请重新输入");
}
}
//更新学生
public void update(Student student) {
if (student != null) {
int index = findByIndex(student.getId());
//判断
if (index != -1) {
array[index] = student;
}else{
System.out.println("查无此学生");
}
} else {
System.out.println("请输入学生信息");
}
}
//显示
public void findAll(){
//加强for循环
for (Student student : array) {
if (student!=null) {
System.out.println("id="+student.getId()+"\t username="+
student.getUsername()+"\t sex="+
student.getSex()+"\t age="+
student.getAge());
}
}
}
}
5.3.2.4 前端控制类
- action:负责接收用户的数据,并且封装到实体类对象中
- 前端
import java.util.Scanner;
public class StudentAction {
//依赖于业务层
private StudentService service = new StudentService();
//保存学生
public void saveStudent(Scanner scanner){
//创建对象
Student student = new Student();
student.setId(StudentUtils.getKetInt());
System.out.println("请输入姓名");
student.setUsername(scanner.next());
System.out.println("请输入性别");
student.setSex(scanner.next());
System.out.println("请输入年龄");
student.setAge(scanner.nextInt());
service.save(student);
//提示
System.out.println("操作成功");
}
//显示学生
public void show(){
service.findAll();
}
//删除学生
public void deleteStudent(Scanner scanner){
System.out.println("请输入学号");
service.delete(scanner.nextInt());
}
//更新学生
public void updateStudent(Scanner scanner){
//提示
System.out.println("请输入学号");
Student student = new Student();
student.setId(scanner.nextInt());
System.out.println("请输入新姓名");
student.setUsername(scanner.next());
System.out.println("请输入新性别");
student.setSex(scanner.next());
System.out.println("请输入新年龄");
student.setAge(scanner.nextInt());
//调用更新
service.update(student);
System.out.println("操作成功");
}
}
5.3.2.5 工具类
- 提供一个不重复的id
- 提供一个日期
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.zip.CRC32;
public class StudentUtils {
//构造方法私有化
private StudentUtils(){}
//静态方法
public static String getKetString(){
return UUID.randomUUID().toString();
}
//静态方法
public static int getKetInt(){
CRC32 crc32 = new CRC32();
crc32.update(UUID.randomUUID().toString().getBytes());
long value = crc32.getValue();
//转换
return (int) value;
}
//日期
public static String getDateString(){
return LocalDateTime.now().toString();
}
}
5.3.2.6 前端类
- 前端
import java.util.Scanner;
public class StudentFront {
public static void main(String[] args){
//依赖于action层
StudentAction action = new StudentAction();
//控制台
Scanner scanner = new Scanner(System.in);
//循环
boolean isRunning = true;
System.out.println("1添加;2显示;3删除;4更新;5退出;");
do{
int key = scanner.nextInt();
switch(key){
case 1:
action.saveStudent(scanner);
case 2:
action.show();
break;
case 3:
action.deleteStudent(scanner);
break;
case 4:
action.updateStudent(scanner);
break;
case 5:
isRunning = false;
break;
}
}while(isRunning);
}
}
5.4 类的继承
5.4.1 继承:
- 实现了代码重用率;例如:A继承B,那么,A就拥有B的所有特性;
- java中的类是单继承性
- 继承的关键字extends
- 继承优先
- 案例:class A extends B{}
5.4.2 代码重用率
public class Demo {
public static void main(String[] args) {
Student student = new Student();
//可以直接调用父类对象的方法
student.setAge(11);
}
}
class Student extends Person{
private int sid;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
}
class User extends Person{
private int sid;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
}
class Person{
private String username;
private String sex;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 当创建子类对象时,父类对象先被创建出来(无父即无子),子类会默认创建父类的构造方法
public class Demo {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student extends Person{
public Student(){
System.out.println("子类构造方法");
}
}
class Person{
public Person(){
System.out.println("父类构造方法");
}
}

5.4.3 类的重写(覆盖)-都是对方法
- 重载
- 在同一个类中
- 方法名称相同
- 只跟方法的参数:个数、顺序、类型不同即可
- 与方法的修饰符和返回类型无关
- 重写
- 必须是子类和父类有继承关系
- 覆盖只能出现在子类中
- 在子类中被覆盖的方法,必须与父类的方法"完全"一样,也就是说方法名称、返回类型、方法参数,要完全一致
- 子类方法的访问权限不能小于父类方法的访问权限
- 子类不能抛出比父类更多的异常,但是,可以抛出父类异常的子异常
- 父类的静态方法不能被子类覆盖
- 父类的私有方法也不能被覆盖
- 覆盖针对的是"方法"而非"属性"
- 重写就是改变了父类的行为
5.4.3.1 案例:
public class Demo {
public static void main(String[] args) {
Animal cat = new Cat();
cat.move();
}
}
class Animal {
public void move(){
System.out.println("动物移动");
}
}
class Cat extends Animal{
public void move(){
System.out.println("小猫在移动..");
}
}
通过上面的例子,可以看到,父类指向子类的引用,子类改变了父类的行为
多态就是多种形态,overload(重载)是多态的一种,属于编译期绑定;override(重写)是运行期间绑定的(后期绑定)
多态存在的条件:
- 有继承
- 有覆盖
- 父类指向子类的引用
5.4.3.2 对静态方法覆盖
public class Demo {
public static void main(String[] args) {
Animal cat = new Cat();
cat.move();
}
}
class Animal {
public static void move(){
System.out.println("动物移动");
}
}
class Cat extends Animal{
public static void move(){
System.out.println("小猫在移动..");
}
}
- 静态方法不产生覆盖

5.4.3.3 成员方法可以被覆盖,但是成员变量不能被覆盖
public class Demo {
public static void main(String[] args) {
Animal cat = new Cat();
cat.move();
System.out.println(cat.name);
}
}
class Animal {
public String name = "动物";
public void move(){
System.out.println("动物移动");
}
}
class Cat extends Animal{
public String name = "动物";
public void move(){
System.out.println("小猫在移动..");
}
}
5.5 super关键字
5.5.1 作用
- super指的是父类对象
- 调用父类的构造方法
- 调用父类的成员方法
注意:super只能应用于成员方法和构造方法中,不能应用在static静态方法中,如果在构造方法中使用(和this相同)必须放在首行。
5.5.2 为什么会有super关键字
- 因为子类创建对象时必须调用父类的构造方法,先把父类构造完成,因为子类依赖于父类;无父即无子;
- 子类有时也调用父类的成员方法;
- 特殊注意:我们在很多的时候创建子类对象时,并不显示的编写构造方法的,那么,默认调用的就是父类的无参数构造方法
- 特别注意:构造方法可以被重载,不可以被重写
5.5.3 案例:
5.5.3.1 调用父类的默认构造方法
public class Demo {
public static void main(String[] args) {
//创建子类对象
Emp emp = new Emp();
}
}
class Person{
public Person(){
System.out.println("父类的无参数构造方法");
}
}
class Emp extends Person{
public Emp(){
//super();
System.out.println("子类的无参数构造方法");
}
}
- 先创建父类对象然后创建子类对象
5.5.3.2 调用指定的构造方法
public class Demo {
public static void main(String[] args) {
//创建子类对象
Emp emp = new Emp();
}
}
class Person{
//构造方法重载
public Person(){
System.out.println("父类的无参数构造方法");
}
public Person(int i){
System.out.println("父类1参数构造方法");
}
public Person(int i,int n){
System.out.println("父类2参数构造方法");
}
}
class Emp extends Person{
public Emp(){
super(1,2);
System.out.println("子类的无参数构造方法");
}
}
- 调用父类的2个参数的构造方法
5.5.3.3 笔试题

5.5.3.4 笔试题
public class Demo {
public static void main(String[] args) {
//创建子类对象
Emp emp = new Emp(10);
}
}
class Person{
//构造方法重载
public Person(){
System.out.println(1);
}
public Person(int i){
System.out.println(2);
}
static{
System.out.println(3);
}
{
System.out.println(4);
}
}
class Emp extends Person{
public Emp(){
System.out.println(5);
}
public Emp(int i){
System.out.println(6);
}
static{
System.out.println(7);
}
{
System.out.println(8);
}
}
- 输出:374186
5.6 final
- final表示最终的,不能改变的
5.6.1 修饰
- 采用final修饰的类不能被继承了
- 采用final修饰的变量:
- 基本类型:变量的值不能改变
- 引用类型:引用的地址不能改变,但是被指向的对象中的属性是可以修改的
- 采用final修饰的方法不能被覆盖了
- 采用final修饰的变量必须显示初始化
- 构造方法不能被final修饰
- 使用static final修饰的是"常量",名称要求"全部"大写
5.6.2 案例:
5.6.2.1 final的类不能被继承

5.6.2.2 final修饰的方法不能被覆盖

5.6.2.3 final修饰基本类型变量

- 常量

- 必须初始化

5.6.2.4 final修饰引用类型地址不能被改变

- 可以修改对象中属性
public class Demo {
public static void main(String[] args) {
final Person person = new Person();
//修改属性
person.setUsername("张三");
person.setUsername("李四");
}
}
class Person{
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
5.7 抽象类
- 重点:抽象类不能创建对象,但是,有构造方法因为子类在创建对象时必须调用父类的构造方法。
5.7.1 抽象类
- 使用abstract修饰的类即为抽象类
- 使用abstract修饰的方法即为抽象方法
- 抽象类中可以有抽象方法也可以没有
- 如果类中有抽象方法,那么这个类一定是抽象类
- 抽象方法不能使用final修饰
- 子类继承了抽象类,那么必须实现所有的抽象方法
- 如果子类继承了抽象父类,并且,没有实现抽象方法,那么这个子类也一定是一个抽象类
5.7.2 抽象类的作用
- 在抽象类中可以添加公用的代码(就是相同的代码)
- 在抽象类中可以约束
5.7.3 案例:
5.7.3.1 抽象父类
public class Demo {
public static void main(String[] args) {
new Emp();
}
}
abstract class Person{
private int id;
private String username;
public Person(){
System.out.println("------------>>");
}
public Person(int i){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
class Emp extends Person{}
- 子类创建对象时也调用了父类的构造方法
5.7.3.2 在抽象父类中添加约束
- 添加一个抽象方法,子类必须要实现
abstract class Person{
//抽象方法
public abstract void show();
public abstract void save();
//实现的方法
public void update(){
System.out.println("实现的方法");
}
}
class Emp extends Person{
@Override
public void show() {
System.out.println("必须实现抽象方法");
}
@Override
public void save() {
System.out.println("必须实现抽象方法");
}
}
5.7.3.3 抽象父类不能使用final修饰
- 因为这样很矛盾(这两个关键字)

5.7.3.4 抽象方法不能使用final修饰
- 因为这样很矛盾(这两个关键字)

5.7.3.5 抽象类中的抽象方法没有被实现,那么,子类也是抽象类
public class Demo {
public static void main(String[] args) {
new Emp();
}
}
abstract class Person{
//抽象方法
public abstract void show();
}
abstract class Emp extends Person{
public abstract void save();
}
//最终的子类必须实现所有的抽象方法
class User extends Emp{
@Override
public void show() {
}
@Override
public void save() {
}
}
5.8 接口
- 接口就是标准,关键字是interface
- 面向抽象编程,就是面向接口编程
5.8.1 接口分类
5.8.1.1 普通接口:
- 接口中的方法都是public abstract
- 接口中的方法可以实现
- 接口中的的变量都是常量public static final
- 接口不能实例化,接口中没有构造方法的概念
- 接口间可以实现多继承,但是,接口之间不能实现
- 实现接口的关键字implements
- 类可以实现多个接口,并且,需要实现接口中所有的抽象方法
5.8.1.2 函数式接口:
- 使用标签:FunctionInterface修饰的
- 接口中可以有实现的方法了
- 接口中只能有且只有一个必须实现的方法
5.8.2 创建接口
5.8.2.1 编写接口
public interface StudentManager {
public abstract void save();
public static final int ABC = 100;
//实现的方法:使用default关键字就是让你来覆盖的
default void delete(){
System.out.println("这是一个删除方法");
}
}
- 编写接口时可以省略public abstarct或者static final
public interface StudentManager {
void save();
int ABC = 100;
}
5.8.2.2 接口中的变量都是常量,所以不能修改

5.8.2.3 接口之间不能实例化

5.8.2.4 接口间可以多继承
interface A{
void method1();
}
interface B{
void method2();
}
interface C extends A,B{}
5.8.2.5 接口间不能实现

5.8.2.6 接口只能通过类实现,并且,需要实现所有的抽象方法
interface A{
void method1();
}
interface B {
void method2();
}
interface C extends A,B{}
//实现接口
class CImpl implements C{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
5.8.2.7 一个类可以同时实现多个接口
interface A{
void method1();
}
interface B {
void method2();
}
//实现接口
class CImpl implements A,B{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
5.8.2.8 即有父类也要实现接口,继承优先
//父类
class Person{}
interface A{
void method1();
}
interface B {
void method2();
}
//实现接口
class CImpl extends Person implements A,B{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
5.8.2.9 笔试题:抽象类可以实现接口
- 无非就是添加接口中的抽象方法
interface A{
void show();
}
//父类
abstract class Person implements A{
}
//实现抽象方法
class CImpl extends Person{
@Override
public void show() {
}
}
5.8.3 接口进一步应用
5.8.3.1 接口的本质
- 本质就是抽象
- 采用接口可以很明确的声明了所提供的服务
- 解决了类的单继承问题
- 重要:实现了可插拔性
5.8.3.2 开发一个系统需要使用两种数据库:mysql、oracle
- 第一种:不使用接口
public class Demo {
public static void main(String[] args) {
//使用mysql数据库
StudentMysql mysql = new StudentMysql();
mysql.add();
//使用oracle数据库
StudentOracle oracle = new StudentOracle();
oracle.save();
}
}
//操作mysql
class StudentMysql{
public void add(){
System.out.println("保存数据-mysql");
}
public void show(){
System.out.println("显示数据-mysql");
}
}
//操作oracle
class StudentOracle{
public void save(){
System.out.println("保存数据-oracle");
}
public void findAll(){
System.out.println("显示数据-oracle");
}
}
- 代码不能灵活使用,当我们的需求改变了需要改动大量代码的,这样会导致代码重构,因为项目没有扩展性。
- 第二种:使用接口

public class Demo {
public static void main(String[] args) {
//使用mysql数据库
IStudent iStudent = new StudentOracleImpl();//new StudentMysqlImpl();
iStudent.save();
iStudent.delete();
iStudent.update();
}
}
//定义标准
interface IStudent{
void save();
void delete();
void update();
}
//操作mysql
class StudentMysqlImpl implements IStudent{
public void save(){
System.out.println("保存数据-mysql");
}
public void delete(){
System.out.println("删除数据-mysql");
}
public void update(){
System.out.println("更新数据-mysql");
}
}
//操作oracle
class StudentOracleImpl implements IStudent{
public void save(){
System.out.println("保存数据-oracle");
}
public void delete(){
System.out.println("删除数据-oracle");
}
public void update(){
System.out.println("更新数据-oracle");
}
}
- 体现了扩展性和灵活性:
- 只要符合标准都可以随时更换
- 面向接口编程(因为,我们以后做每一件事情都要遵守标准和规范)
5.9 类之间的关系
5.9.1 泛化关系:类和类之间的继承及接口之间的继承

5.9.2 实现关系:类对接口的实现

5.9.3 关联关系:
- 类与类之间的连接,一个类作为另一个类的属性和方法使用,例如:实例变量(成员属性)

5.9.4 聚合关系
- 聚合关系是关联关系的一种,是较强的一种关联关系;例如:汽车和轮胎,一个代表整体,一个代表局部,但是,符合汽车的标准即可,不需要指定轮胎的厂家

5.9.5 合成关系:
- 合成关系是关联关系的一种,比如聚合关系强的一种关联关系;例如:人和四肢

5.9.6 依赖关系:是比关联关系弱的一种关系

6.0 is-a、is-like-a、has-a
6.0.1 is-a:接口中有的方法,类中必须实现
interface A{
void method();
}
class AImpl implements A{
@Override
public void method() {
}
}
6.0.2 is-like-a:接口中的有的方法,类中必须实现,并且,类中也有自己的方法
interface A{
void method();
}
class AImpl implements A{
@Override
public void method() {
}
public void method2(){
}
}
6.0.3 has-a:一个类作为另一个类的属性使用
class B{}
class AImpl{
private B b;
}
6.1 Object类
- Object类是所有类的父类(基类、超类)
- 自定义类不显示的继承Object,那么,默认的基类就是Object
6.1.1 案例

- 相当于

6.1.2 toString方法
public class Demo {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.toString());
}
}
class Person{
@Override
public String toString() {
return super.toString();
}
}

- 源代码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 注意:在输出对象的参数时候,调用或不调用toString方法默认都在执行toString方法:
"getClass().getName() + "@" + Integer.toHexString(hashCode());"
6.1.2.1 我们在调用对象的时候,都习惯把toString方法"覆盖"
public class Demo {
public static void main(String[] args) {
Person person = new Person();
person.setUsername("张三");
person.setSex("男");
person.setAge(11);
//输出
System.out.println(person.toString());
}
}
class Person{
private String username;
private String sex;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"username='" + username + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}

6.1.3 finalize方法
- 垃圾回收器(Garbage Collection) GC
- 当对象不在使用了,那么,GC会将其回收
- 垃圾回收在后台运行,我们没有办法命令立刻回收,但是,可以通知
- 垃圾回收前必须调用的方法就是finalize,建议不使用这个方法
- 案例:
public class Demo {
public static void main(String[] args) {
//创建对象
Person person = new Person();
//不使用了
person = null;
//通知垃圾回收器
System.gc();
}
}
class Person{
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾回收前调用了");
}
}
- 扩展
public class Demo {
public static void main(String[] args) {
//创建对象
Person person = new Person();
//使用对象后
person = null;
}
}
在使用完后是否需要把对象设置为null?
答:不需要设置为null,因为,在方法中的参数都是在压栈时存在,弹栈时不存在;
6.1.4 ==与equals方法
6.1.4.1 等号“==”
- 基本类型:比较的是"值"
- 引用类型:比较的是"址"(内存地址)
案例1:基本类型
public class Demo extends Object{
public static void main(String[] args) {
//创建对象
Person person = new Person();
//使用对象后
person = null;
}
}

案例2:引用类型
public class Demo {
public static void main(String[] args) {
//创建对象
Person person = new Person();
person.setId(1001);
person.setUsername("张三");
//创建对象
Person person1 = new Person();
person1.setId(1001);
person1.setUsername("张三");
//比较
System.out.println("person="+person);
System.out.println("person1="+person1);
System.out.println(person==person1);
}
}
class Person{
private int id;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}


案例3:引用类型--进一步完善
- equals方法默认使用的是"=="
jdk的源代码
public boolean equals(Object obj) {
return (this == obj);
}
- 需要把引用类型中的值比较一下:id和username的"值"相同,那么,就认为同一个人

- String的源代码
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
- 覆盖Object中的equals方法
public class Demo {
public static void main(String[] args) {
//创建对象
Person person = new Person();
person.setId(1001);
person.setUsername("张三");
//创建对象
Person person1 = new Person();
person1.setId(1001);
person1.setUsername("张三");
//比较
System.out.println(person==person1);
System.out.println(person.equals(person1));
}
}
class Person{
private int id;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public boolean equals(Object anObject) {
//如果两个对象的地址相同就是true
if (this == anObject) {
return true;
}
//判断是否为指定的类型
if (anObject instanceof Person) {
Person another = (Person)anObject;
//判断id和username
if (id==another.getId() && username.equals(((Person) anObject).getUsername())){
return true;
}
}
return false;
}
}
- instanceof判断类型使用的
- 注意:强制类型转换
- 将父类转为子类"向下转型",是不安全的
- 将子类转为父类"向上转型",是安全的
6.2 访问权限
修饰符 | 类的内部 | 同一个包 | 子类(不同包) | 任何位置 |
---|---|---|---|---|
public | Y | Y | Y | Y |
private | Y | N | N | N |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
6.3 内部类
6.3.1 分类
- 实例内部类:必须持有外部的引用(实例)
- 静态内部类:不需要持有外部的引用(实例)
- 局部内部类:在方法中使用
6.3.2 实例内部类
- 必须持有外部的引用(实例):就是内外两个对象都创建
- 内部不能定义static成员,只能是实例成员
- 案例:第一种方式
public class Demo {
public static void main(String[] args) {
//使用内部类
A a = new A();
a.show(); //调用自己的成员方法
//再创建内部的对象
A.B b = a.new B();
b.save();
}
}
class A{
//成员方法
public void show(){
System.out.println("A--method");
}
class B{//内部类
public void save(){
System.out.println("B--method");
//可以调用外部的成员方法
show();
}
}
}
- 第二种方式
public class Demo {
public static void main(String[] args) {
//直接创建对象
A.B b = new A().new B();
b.save();
}
}
class A{
//成员方法
public void show(){
System.out.println("A--method");
}
class B{//内部类
public void save(){
System.out.println("B--method");
//可以调用外部的成员方法
show();
}
}
}
6.3.3 静态内部类
- 不需要持有外部的引用
- 静态内部类可以访问外部的静态变量,如果需要访问外部的成员变量必须通过外部的对象方法
- 案例:
public class Demo {
public static void main(String[] args) {
//不需要外部创建对象,直接创建内部对象即可
A.B b = new A.B();
b.save();
//可以直接调用静态方法
System.out.println(A.B.i);
}
}
class A{
//静态方法
public static void show(){
System.out.println("A--method");
}
static class B{//内部类
public static int i =100;
public void save(){
System.out.println("B--method");
//可以调用外部的成员方法
show();
}
}
}
6.3.4 局部内部类
- 就是在方法中创建的类;和局部变量的使用是相同的
public class Demo {
public static void main(String[] args) {
//创建对象,调用成员方法
new A().method(100);
}
}
class A{
public void method(int i){
//相当于自定义了一种只在方法内使用的类型
class B{
public void save(){
System.out.println("----->>"+i);
}
}
//在方法内使用
B b = new B();
b.save();
}
}
6.3.5 扩展:匿名内部类
- 接口作为方法的匿名使用
public class Demo {
public static void main(String[] args) {
B b = new B();
//调用方法,匿名实现接口中的方法
Object exec = b.exec(new A() {
@Override
public Object callBack() {
return "匿名实现";
}
});
System.out.println(exec);
}
}
interface A{
Object callBack();
}
class B{
//成员方法
public Object exec(A a){
return a.callBack();
}
}
- 使用lambda函数
public class Demo {
public static void main(String[] args) {
A a = ()->"lambda函数方式实现";
//调用
Object back = a.callBack();
System.out.println(back);
}
}
interface A{
Object callBack();
}