我们现在所学的面向对象,相当于是一个一个的小螺丝钉
学了这些小螺丝钉,肯定还不知道具体能解决什么样的问题

所以,必须在学习完这些小螺钉之后,将这些综合起来,去造火箭、航母、飞机 …

结论:
多关注语法点的基本作用 ——> 多进行思考和记忆、练习 ——> 要自信,不要在短期想能做什么?
static关键字:
之前定义的成员变量:name, age属于每个对象的。
如何表示共享的信息?如在线人数等。
设计模式:单例
面向对象三大特征之二:继承
static是 静态 的意思,可以修饰成员变量和成员方法。
static修饰成员变量表示该成员变量 只在内存中存储一份 ,可以被 共享访问、修改。
举例:
比如,bilibili的在线观看人数这个成员变量,是不是每个用户对象进入bilibili都可以看到这个在线观看人数;
那么在程序中,这个成员变量就可以用static来修饰,只需要存储一份,可以被共享访问、修改。

静态成员变量(有static修饰,属于类,内存中只加载一次):
常表示在:如bilibili的在线观看人数、已装填弹幕数等被共享的信息,可以被共享访问。
静态成员变量的调用:
类名.静态成员变量; (推荐)
对象.静态成员变量; (不推荐)
用户类
package com.app.d1_static;
// 用户类
public class User {
/**
静态成员变量:在线人数、弹幕数
注意:有static修饰的成员变量:静态成员变量,只在内存中存储一份,可以被共享
*/
public static int onlineUsers = 22;
public static int barrageNumber = 342;
// main主方法
public static void main(String[] args) {
// 目标:理解static修饰成员变量的作用和访问特点
// 静态成员变量的调用:
System.out.println("静态成员变量的调用: ");
// 1.调用:类名.静态成员变量; (推荐)
System.out.println(User.onlineUsers + " 人正在观看,已装填 " + User.barrageNumber + " 条弹幕");
System.out.println("----------------------------");
// 2.调用:对象.静态成员变量; (不推荐)
User user = new User();
// 不推荐的原因是因为在用 对象. 的时候不会显示静态成员变量,但是也不影响使用
user.onlineUsers++; // 又有一个人来观看视频
user.barrageNumber++; // 这个人发了一条弹幕
System.out.println(user.onlineUsers + " 人正在观看,已装填 " + user.barrageNumber + " 条弹幕");
}
}
控制台:
静态成员变量的调用:
22 人正在观看,已装填 342 条弹幕
----------------------------
23 人正在观看,已装填 343 条弹幕
Process finished with exit code 0
实例成员变量(无static修饰,存在于每个对象中):
常表示姓名name、年龄age、等属于每个对象的信息。
实例成员变量的调用:
对象.实例成员变量;
用户类
package com.app.d1_static;
// 用户类
public class User {
/**
静态成员变量:在线人数、弹幕数
注意:有static修饰的成员变量:静态成员变量,只在内存中存储一份,可以被共享
*/
public static int onlineUsers = 22;
public static int barrageNumber = 342;
/**
实例成员变量:姓名、年龄
注意:无static修饰的成员变量:实例成员变量,属于每个对象的,必须用对象名.访问
*/
public String name;
public int age;
public static void main(String[] args) {
// 目标:理解static修饰成员变量的作用和访问特点
// 静态成员变量的调用:
System.out.println("静态成员变量的调用: ");
// 1.调用:类名.静态成员变量; (推荐)
System.out.println(User.onlineUsers + " 人正在观看,已装填 " + User.barrageNumber + " 条弹幕");
System.out.println("----------------------------");
// 2.调用:对象.静态成员变量; (不推荐)
User user = new User();
// 不推荐的原因是因为在用 对象. 的时候不会显示静态成员变量,但是也不影响使用
user.onlineUsers++; // 又有一个人来观看视频
user.barrageNumber++; // 这个人发了一条弹幕
System.out.println(user.onlineUsers + " 人正在观看,已装填 " + user.barrageNumber + " 条弹幕");
System.out.println();
// 实例成员变量的调用:
System.out.println("实例成员变量的调用:");
// 新增一个用户
User user1 = new User();
// 3.调用:对象.实例成员变量
// User.name = "张飞"; // 报错!因为实例成员变量属于每个对象
user1.name = "关羽";
user1.age = 25;
User.onlineUsers++; // 关羽来观看视频啦!
User.barrageNumber++; // 关羽发了一条弹幕
System.out.println(user1.name + "今年" + user1.age + "岁了!");
System.out.println("----------------------------");
// 新增一个用户
User user2 = new User();
// 3.调用:对象.实例成员变量
user2.name = "刘备";
user2.age = 39;
User.onlineUsers++; // 关羽来观看视频啦!
User.barrageNumber++; // 关羽发了一条弹幕
System.out.println(user2.name + "今年" + user2.age + "岁了!");
System.out.println();
// 静态成员变量的调用
System.out.println(User.onlineUsers + " 人正在观看,已装填 " + User.barrageNumber + " 条弹幕");
System.out.println();
// 静态成员变量的访问方式1:对象名访问(不推荐)
System.out.println("静态成员变量的访问方式1: " + user1.onlineUsers);
// 静态成员变量的访问方式2:类名访问(推荐方式)
System.out.println("静态成员变量的访问方式2: " + User.onlineUsers);
// 注意:静态成员变量的访问方式3 ——>同一类中,访问静态成员变量,类名可以省略不写。但只局限于此类中访问。
System.out.println("静态成员变量的访问方式3: " + onlineUsers);
}
}
控制台
静态成员变量的调用:
22 人正在观看,已装填 342 条弹幕
----------------------------
23 人正在观看,已装填 343 条弹幕
实例成员变量的调用:
关羽今年25岁了!
----------------------------
刘备今年39岁了!
25 人正在观看,已装填 345 条弹幕
静态成员变量的访问方式1: 25
静态成员变量的访问方式2: 25
静态成员变量的访问方式3: 25
Process finished with exit code 0
1、成员变量的分类和访问分别是如何的?
静态成员变量:
有static修饰,属于类,在内存中只存储一份,可以被共享访问
访问格式
类名.静态成员变量; // 推荐(方便)
对象名.静态成员变量; // 不推荐(不方便)
实例成员变量:
无static修饰,属于每个对象的
访问格式
对象名.实例成员变量;
2、两种成员变量各自在什么情况下定义?
第一,将User.java文件 编译成 字节码文件User.class ,编译后的User.class文件和main方法 加载到方法区;
第二,将main方法加载到栈内存中执行,然后将 static修饰的静态成员变量onLineNumber加载到堆内存中的静态变量区,此时onLineNumber属于被共享了;
第三,执行main方法里的第一句:System.out.println(User.onLineNumber); ——> 直接输出 访问到的静态成员变量的值到控制台:161;
第四,执行main方法里的第二句:System.out.println(onLineNumber); ——>直接输出 访问到的静态成员变量的值到控制台:161;
第五,执行main方法里的第三句:User.onLineNumber++; ——>对静态成员变量onLineNumber进行自增,此时值变成了:162;
第六,执行main方法里的第四句:System.out.println(name); ——>因为这个 name 是 无static 修饰的,属于实例成员变量,因此它属于每个对象,但是由于目前没有将对象创建出来,就直接访问实例成员变量:name,所以这句代码执行失败!报错!!所以将这句代码注释起来了,为了不影响后面的代码的执行;

第七,执行main方法里的第五句:User u1 = new User(); ——>先执行 “=” 右边的代码:在堆内存中创建一个用户对象并产生一个内存地址,然后将这个用户对象的内存地址 赋值给 “=” 左边的对象名:u1;
第八,执行main方法里的第六句:u1.name = "孙悟空"; ——>将 孙悟空 赋值给 对象名: u1 ,u1 再根据地址找到用户对象的成员变量name,进行赋值;
第九,执行main方法里的第七句:u1.age = 35; ——> 将 35 赋值给 对象名: u1 ,u1 再根据地址找到用户对象的成员变量age,进行赋值;
第十,执行main方法里的第八句:System.out.println(u1.name); ——> u1 根据地址找到用户对象的成员变量name,将值输出到控制台;
第十一,执行main方法里的第九句:System.out.println(u1.age); ——> u1 根据地址找到用户对象的成员变量age,将值输出到控制台;
第十二,执行main方法里的第十句:u1.onLineNumber++; ——> u1 根据地址找到对象,由于静态变量区在堆内存中是被共享的,所以找到静态成员变量onLineNumber 后进行自增的操作,值162+1后得:163;

第十三,执行main方法里的第十一句:User u2 = new User(); ——>先执行 “=” 右边的代码:在堆内存中创建一个用户对象并产生一个内存地址,然后将这个用户对象的内存地址 赋值给 “=” 左边的对象名:u2;
第十四,执行main方法里的第十二句:u2.name = "猪八戒"; ——>将 猪八戒 赋值给 对象名: u2 ,u2 再根据地址找到用户对象的成员变量name,进行赋值;
第十五,执行main方法里的第十三句:u2.age = 29; ——> 将 29 赋值给 对象名: u2 ,u2 再根据地址找到用户对象的成员变量age,进行赋值;
第十六,执行main方法里的第十四句:System.out.println(u2.name); ——> u2 根据地址找到用户对象的成员变量name,将值输出到控制台;
第十七,执行main方法里的第十五句:System.out.println(u2.age); ——> u2 根据地址找到用户对象的成员变量age,将值输出到控制台;
第十八,执行main方法里的第十六句:u2.onLineNumber++; ——> u2 根据地址找到对象,由于静态变量区在堆内存中是被共享的,所以找到静态成员变量onLineNumber 后进行自增的操作,值163+1后得:164;

第十九,执行main方法里的第十七句:System.out.println(User.onLineNumber); ——> 直接输出 访问到的静态成员变量的值到控制台:164。

静态成员方法(有static修饰,属于类),建议用类名访问,也可以用对象名访问。
实例成员方法(无static修饰,属于对象),只能用对象出发访问。
学生类
package com.app.d1_static;
/**
学生类
目标:理解static修饰成员方法的作用和访问特点。
*/
public class Student {
/**
实例成员变量:无static修饰,属于对象,必须使用对象名触发访问。
*/
private String name;
/**
静态成员方法:有static修饰,属于类,可以被共享访问,用类名或者对象名都可以触发访问。
*/
public static int getMax(int age1, int age2) {
return age1 > age2 ? age1 : age2;
}
/**
实例成员方法:无static修饰,属于对象,只能用对象名触发访问。
*/
public void study() {
// 注意:因为name是实例成员变量,所以也属于对象,可以直接访问使用!!
System.out.println(name + "照镜子,里外不是人!");
}
// main主方法
public static void main(String[] args) {
// 静态成员方法的调用
System.out.println("静态成员方法的调用:");
// 1.类名.静态成员方法
System.out.println(Student.getMax(20, 19)); // 20
// 注意:同一个类中,访问静态成员方法,类名可以省略不写。但只局限于此类中访问。
System.out.println(getMax(19, 23)); // 23
System.out.println();
// 实例成员方法的调用
System.out.println("实例成员方法的调用:");
// 2.对象名.实例成员方法
Student s1 = new Student();
s1.name = "猪八戒";
s1.study();
// 报错!!因为study()方法无static修饰,是实例成员方法,属于对象,不能用类名触发访问调用。
// Student.study();
// study();
// 3.对象名.静态成员方法(语法是可行的,但是不推荐)
System.out.println(s1.getMax(14, 55));
}
}
静态成员方法的调用:
20
23
实例成员方法的调用:
猪八戒照镜子,里外不是人!
55
Process finished with exit code 0
1、成员方法的分类和访问分别是什么样的?
静态成员方法:
有static修饰,属于类和对象共享,类名和对象名都可以触发访问;
访问格式:
类名.静态成员方法; // 推荐
对象名.静态成员方法; // 不推荐
实例成员方法:
无static修饰,属于对象,必须使用对象名触发访问;
访问格式:
对象名.实例成员方法;
2、每种成员方法的适用场景是什么样的?
第一,将Student.java文件 编译成 Student.class字节码文件,编译后的Student.class字节码文件 和 被static修饰的main()、getMax()方法 加载到方法区;
第二,执行main方法里的第一句:System.out.println(Student.getMax(10, 2)); ——> 由于getMax() 方法是被 static 修饰的,所以是共享的,可以直接通过 类名触发调用该方法,然后将 10和2 这两个实参 传送给 getMax() 方法的两个形参,接收到实参后,判断 10是否大于2 ,为true返回10;为false返回2, 判断结果为true,所以返回10;此时控制台显示10;
第三,执行main方法里的第二句:System.out.println(getMax(2, 10)); ——> 由于getMax() 方法是被 static 修饰的,所以是共享的,而且又在同一个类中,所以可以直接调用该方法,然后将 2和10 这两个实参 传送给 getMax() 方法的两个形参,接收到实参后,执行getMax()方法的第一句:return a > b ? a : b; ——>判断 2是否大于10 ,为true返回2;为false返回10, 判断结果为false,所以返回10;此时控制台显示10;
第四,执行main方法里的第三句:study(); ——> 因为这个 study() 方法是 无static 修饰的,属于实例成员方法,因此它属于每个对象,但是由于目前没有将对象创建出来,就直接调用实例成员方法:study() ,所以这句代码执行失败!报错!!所以将这句代码注释起来了,为了不影响后面的代码的执行;

第五,执行main方法里的第四句:Student s = new Student(); ——> 先执行 “=” 右边的代码:在堆内存中创建一个学生对象并产生一个内存地址,然后将这个学生对象的内存地址 赋值给 “=” 左边的对象名:s;
第六,执行main方法里的第五句:s.name = "猪八戒"; ——> 将 猪八戒 赋值给 对象名: s ,s 再根据地址找到学生对象的成员变量:name,进行赋值;
第七,执行main方法里的第六句:s.study(); ——> s 根据地址找到学生对象,然后根据里面存的实例成员方法的地址找到 study() 方法,进行调用,将study() 方法加载到栈内存中执行的第一句:System.out.println(name + "在好好学习~~~"); ——> 由于 name 是无static 修饰的,属于实例成员变量,所以可以在实例成员方法study() 中直接调用,在控制台输出:猪八戒在好好学习~~~;
第八,执行main方法里的第七句:System.out.println(s.getMax(20, 10)); ——> 由于getMax()方法是有static修饰的,属于静态成员方法,是被共享的;因此,可以使用对象名s触发调用getMax()方法,将20和10这两个实参 传送给 getMax() 方法的两个形参,接收到实参后,执行getMax()方法的第一句:return a > b ? a : b; ——>判断 20是否大于10,为true返回20;为false返回10,判断结果为true,返回20,所以控制台显示:20;

静态成员方法只能访问静态的成员,不能 “直接” 访问实例的成员。
实例成员方法可以访问静态的成员,也可以访问实例的成员。
静态方法中是不可以出现this关键字的。
package com.app.d1_static;
// 目标:理解static访问相关的语法:面试笔试题,或者以后理解程序很重要的知识[拓展]
public class Test3 {
/**
静态成员:有static修饰的变量或方法
*/
public static int onlineUsers = 11;
public static void test() {
System.out.println("静态成员方法被调用啦!!");
}
/**
实例成员:无static修饰的变量或方法
*/
private String name;
private void sports() {
System.out.println(name + "正在跑步~~~");
}
// 1. 静态成员方法只能访问静态的成员,不能 “直接” 访问实例的成员
public static void showMovie() {
// (1) 访问静态的成员
// 同一个类中,访问时可以省略类名不写
test();
System.out.println(onlineUsers);
Test3.test();
System.out.println(Test3.onlineUsers);
// (2) 直接访问实例的成员
// 报错!!不能 “直接” 访问实例的成员
// System.out.println(name + "正在打篮球~~");
// sports();
// (3) 可以间接访问实例的成员
Test3 t = new Test3();
System.out.println(t.name);
t.sports();
}
// 2.实例成员方法可以访问静态的成员,也可以访问实例的成员
public void eat() {
// 1.访问静态成员
test();
System.out.println(onlineUsers);
Test3.test();
System.out.println(Test3.onlineUsers);
// 2.访问实例成员
sports();
System.out.println(name);
System.out.println(this);
System.out.println(this.name);
}
// 3.静态方法中是不可以出现this关键字的
public static void test2() {
// 报错!!因为this代表的是当前对象,因此属于对象,是不共享的,所以不能出现this。
// System.out.println(this);
}
}
在企业的管理系统中,通常需要在一个系统的很多业务处使用验证码进行防刷新等安全控制。

所以,当用户登录时,需要一个验证码的功能;然后为了防止刷新等恶意操作,又需要一个验证码的功能:
package com.app.d2_static_util;
import java.util.Random;
public class Login {
public static void main(String[] args) {
// 开发一个验证码
// 1.定义一个变量,用于记住验证码。
String code = "";
// 2.定义一个变量,用于记住全部字符。
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 3.创建一个随机数对象,用于生成一个随机索引
Random rd = new Random();
// 4.定义一个for循环,用于生成几个随机索引,去得到几个字符
for (int i = 0; i < 5; i++) {
// 5.获取随机索引对应的字符,连接给code
int index = rd.nextInt(data.length());
code += data.charAt(index);
}
System.out.println("验证码:" + code);
}
}
package com.app.d2_static_util;
import java.util.Random;
public class Check {
public static void main(String[] args) {
// 开发一个验证码
// 1.定义一个变量,用于记住验证码。
String code = "";
// 2.定义一个变量,用于记住全部字符
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 3.创建一个随机数对象,用于生成一个随机索引
Random rd = new Random();
// 4.定义一个for循环,用于生成几个索引,去得到几个字符
for (int i = 0; i < 5; i++) {
// 5.获取随机索引对应的字符,连接给code
int index = rd.nextInt(data.length());
code += data.charAt(index);
}
System.out.println("验证码:" + code);
}
}
问题就来了:
一是调用方便,二是提高了代码的复用性(一次编写,处处可用)。
工具类
package com.app.d2_static_util;
import java.util.Random;
/**
工具类
*/
public class AppUtil {
/**
静态方法
*/
public static String createVerifyCode(int n) {
// 开发一个验证码
// 1、定义一个变量,记住验证码
String code = "";
// 2、定义一个变量,记住全部可能出现的字符
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 3、创建一个随机数对象,用于生成一个随机索引
Random rd = new Random();
// 4、定义for循环,生成n个索引,去得到n个字符
for (int i = 0; i < n; i++) {
// 5.获取索引对应的字符,连接起来
int index = rd.nextInt(data.length());
code += data.charAt(index);
}
// 6、将生成好的验证码返回
return code;
}
}
package com.app.d2_static_util;
public class Login {
public static void main(String[] args) {
// 开发一个验证码
// 1.调用工具类,得到一个5位的验证码
System.out.println("5位验证码:" + AppUtil.createVerifyCode(5));
// 2.调用工具类,得到一个6位的验证码
System.out.println("6位验证码:" + AppUtil.createVerifyCode(6));
}
}
package com.app.d2_static_util;
public class Check {
public static void main(String[] args) {
// 开发一个验证码
// 1、调用工具类,得到一个5位验证码
System.out.println(AppUtil.createVerifyCode(5));
}
}
控制台
Login类
——————————————————————————
5位验证码:nrhYR
6位验证码:yjvX5y
——————————————————————————
Check类
——————————————————————————
7QNjT
可以看到,我们只需要写一个工具类,其他类要是想用,直接调用就行,想随机几个验证码就随机几个验证码。

由于工具类都是静态方法,直接使用类名触发访问调用,因此,工具类无需创建对象,建议将工具类的构造器使用private修饰:进行私有化。
这样做,是为了防止程序员创建对象,没必要浪费内存;这样做也显得你更专业,严谨。
package com.app.d2_static_util;
import java.util.Random;
/**
工具类
*/
public class AppUtil {
/**
由于工具类都是静态方法,因此需要创建对象,所以把其构造器进行私有化;
显得很专业,严谨。
*/
private AppUtil() {
}
/**
静态方法
*/
public static String createVerifyCode(int n) {
// 开发一个验证码
// 1、定义一个变量,记住验证码
String code = "";
// 2、定义一个变量,记住全部可能出现的字符
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 3、创建一个随机数对象,用于生成一个随机索引
Random rd = new Random();
// 4、定义for循环,生成n个索引,去得到n个字符
for (int i = 0; i < n; i++) {
// 5.获取索引对应的字符,连接起来
int index = rd.nextInt(data.length());
code += data.charAt(index);
}
// 6、将生成好的验证码返回
return code;
}
}
1、工具类是什么?有什么好处?
2、工具类有什么要求?
需求:在实际开发中,经常会遇到一些数组使用的工具类。
请按照如下要求编写一个数组的工具类:ArraysUtils
package com.app.d2_static_util.demo;
/**
练习:设计一个数组的工具类
需求:
1、提供一个工具类方法toString,用于返回整数数组的内容,返回的字符串如:[10, 20, 50, 34, 100]
2、提供一个工具类方法getAerage,用于返回平均值
*/
public class ArraysUtils {
/**
1.私有化构造器,防止创建对象,浪费内存
*/
private ArraysUtils() {}
/**
* 静态方法: toString——用于返回整数数组的内容
* @param arr 用于接收一个数组
* @return 返回整数数组的内容
*/
public static String toString(int[] arr) {
// 1.当传入的数组地址为null时
if (arr == null) {
// 地址为空,返回null
return null;
}
// 2.定义左括号,用于拼接返回结果
String result = "[";
// 3.定义for循环,遍历接收到的数组,取出元素
for (int i = 0; i < arr.length; i++) {
/*
4.拼接遍历到的元素,并且判断是否为最后一个元素:
为true,就不需要加逗号隔开,为false,就需要加逗号隔开
*/
result += (i == arr.length - 1 ? arr[i] : arr[i] + ", ");
}
// 4.表示遍历完了,拼接一个右括号
result += "]";
// 5.拼接完成,返回结果
return result;
}
/**
* 静态方法: getAverage——用于返回平均值
* @param arr 用于接收一个数组
* @return 返回平均值
*/
public static double getAverage(double[] arr) {
// 1.当传入的数组地址为null 或者 数组长度为0时
if (arr == null || arr.length == 0) {
// 地址为null 或者 数组长度为0,返回0
return 0;
}
/*
2.定义变量,分别用于记录: 总分、最大值、最小值
总分: 默认值为0
最大值、最小值: 默认值为数组中的第一个元素
*/
double sum = 0;
double max = arr[0];
double min = arr[0];
// 3.定义for循环,遍历数组,取出所有元素
for (int i = 0; i < arr.length; i++) {
// 4.得到最大值:判断遍历到的元素 是否大于 数组的第一个元素
if (arr[i] > max) {
// 是,则替换最大值
max = arr[i];
}
// 5.得到最小值:判断遍历到的元素 是否小于 数组的第一个元素
if (arr[i] < min) {
// 是,则替换最小值
min = arr[i];
}
// 6.统计总分
sum += arr[i];
}
// 4.循环结束,计算平均分 = (总值 - 最大值 - 最小值) / 个数
double averageValue = (sum - max - min) / (arr.length-2);
// 5.计算完成,返回平均值
return averageValue;
}
}
package com.app.d2_static_util.demo;
/**
测试类
需求:
3、在测试类,调用这些工具类的工具方法,并返回结果
*/
public class TestDemo {
public static void main(String[] args) {
// 1.定义一个数组,传入一组数据,调用工具类方法toString,得到数组内容并输出
int[] arr1 = {10, 20, 50, 34, 100};
System.out.println(ArraysUtils.toString(arr1));
int[] arr2 = null;
System.out.println(ArraysUtils.toString(arr2));
int[] arr3 = {};
System.out.println(ArraysUtils.toString(arr3));
System.out.println("---------------------------------------");
// 2.定义一个数组,传入一组数据,调用工具类方法getAverage,得到平均值并输出
double[] scores = {99.5, 69, 88, 79.5, 78, 55};
System.out.println(ArraysUtils.getAverage(scores));
double[] arr4 = {};
System.out.println(ArraysUtils.getAverage(arr4));
double[] arr5 = null;
System.out.println(ArraysUtils.getAverage(arr5));
}
}
[10, 20, 50, 34, 100]
null
[]
---------------------------------------
78.625
0.0
0.0
Process finished with exit code 0
package com.app.d3_static_code;
/**
目标:先理解静态代码块
*/
public class Demo1 {
public static String schoolName;
public static void main(String[] args) {
System.out.println("main方法执行");
System.out.println(schoolName);
}
/**
静态代码块:有static修饰,属于类,与类一起优先加载,自动触发执行;
作用:可以用于初始化资源。
*/
static {
System.out.println("静态代码块被触发执行了");
schoolName = "应用大学";
}
}
静态代码块被触发执行了
main方法执行
应用大学
Process finished with exit code 0
package com.app.d3_static_code;
/**
目标:理解实例代码块(构造代码块)
*/
public class Demo2 {
private String name;
public Demo2() {
System.out.println("无参构造器被触发执行");
}
/**
实例代码块:无static修饰,属于对象,每次构建对象时,都会先构造器一步执行
作用:初始化实例资源
*/
{
System.out.println("实例代码块被触发执行了");
name = "孙悟空";
}
public static void main(String[] args) {
// 构建对象
Demo2 d1 = new Demo2();
System.out.println(d1.name);
System.out.println("-------------------------------");
Demo2 d2 = new Demo2();
System.out.println(d2.name);
}
}
实例代码块被触发执行了
无参构造器被触发执行
孙悟空
-------------------------------
实例代码块被触发执行了
无参构造器被触发执行
孙悟空
Process finished with exit code 0
案例:斗地主游戏

需求:在进入游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。
分析:
package com.app.d3_static_code;
import java.util.ArrayList;
/**
目标:通过模拟斗地主游戏,更深入的理解静态代码块的作用
需求:
在进入游戏房间的时候,应该提前准备好54张牌,后续才能直接使用这些牌数据
*/
public class StaticTest3 {
/**
1.定义一个静态集合,这样这个集合只会加载一次。因为斗地主房间只需要一副牌。
*/
public static ArrayList<String> cards = new ArrayList<>();
/**
2.在程序真正执行main方法前,把54张牌提前放进集合中,后续游戏房间就可以直接使用了。
*/
static {
// 3.正式做牌,将做好的54张牌放到集合中:
// a、定义一个数组存储牌的全部点数:因为类型确定了,个数确定了,所以用数组会比较专业、优雅。
String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "J", "Q", "K", "A", "2"};
// b、定义一个数组存储牌的全部花色:因为类型确定了,个数确定了,所以用数组会比较专业、优雅。
String[] colors = {"♠", "♥", "♦", "♣"};
// c、定义外部for循环:遍历sizes数组,依次得到牌的点数
for (int i = 0; i < sizes.length; i++) {
// sizes[i]
// d、得到一个牌的点数后,定义内部for循环,遍历colors数组,依次得到牌的花色
// 进行配色:如: ♠3, ♥3, ♦3, ♣3
for (int j = 0; j < colors.length; j++) {
// colors[j]
// 牌的点数 和 牌的花色 进行拼接,得到一张牌后,放进集合中
String card = sizes[i] + colors[j];
cards.add(card);
}
}
// 循环全部结束,表示52张牌的点数已经全部配色完毕,并已放进集合中
// d、将大小王单独放进集合中
cards.add("大🃏");
cards.add("小🃏");
}
public static void main(String[] args) {
// 4、输出新牌
System.out.println("新牌:" + cards);
}
}
新牌:[3♠, 3♥, 3♦, 3♣, 4♠, 4♥, 4♦, 4♣, 5♠, 5♥, 5♦, 5♣, 6♠, 6♥, 6♦, 6♣, 7♠, 7♥, 7♦, 7♣, 8♠, 8♥, 8♦, 8♣, 9♠, 9♥, 9♦, 9♣, J♠, J♥, J♦, J♣, Q♠, Q♥, Q♦, Q♣, K♠, K♥, K♦, K♣, A♠, A♥, A♦, A♣, 2♠, 2♥, 2♦, 2♣, 大🃏, 小🃏]
Process finished with exit code 0
1、静态代码块的作用是什么?
在使用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
package com.app.d4_static_singleinstance;
/**
1.定义一个单例类
*/
public class SingleInstance {
/**
2.单例必须将构造器进行私有化,防止对外产生对象
*/
private SingleInstance() {
}
/**
3.饿汉单例是在获取对象前,已经提前准备好一个对象;
这个对象只能是一个,所以定义静态变量存储
*/
public static SingleInstance instance = new SingleInstance();
}
package com.app.d4_static_singleinstance;
/**
单例测试类
目标:理解饿汉单例的设计步骤
*/
public class SingleInstanceTest1 {
public static void main(String[] args) {
// 获取对象
SingleInstance s1 = SingleInstance.instance;
SingleInstance s2 = SingleInstance.instance;
System.out.println(s1 == s2);
}
}
true
Process finished with exit code 0
1、饿汉单例的实现步骤?
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
package com.app.d4_static_singleinstance;
/**
1.定义一个懒汉单例类
*/
public class SingleInstance2 {
/**
2.必须将构造器私有化:防止对外产生对象。
*/
private SingleInstance2() {
}
/**
3.定义静态变量存储一个对象;
与类一起加载,只加载一次。
注意:最好私有化,这样可以避免给别人挖坑!!因为公开化,别人会拿到一个null。
*/
private static SingleInstance2 instance; // null
/**
* 4.提供一个方法,获取一个单例对象
* @return 返回单例对象
*/
public static SingleInstance2 getInstance(){
/*
如果instance变量没有存储对象,就创建一个对象赋值给instance;
如果instance变量存储有对象,说明不是第一次来拿对象,就不进入if判断,直接将原本的返回。
*/
// a、判断instance变量是否存储有对象
if (instance == null) {
// b、没有存储对象,说明是第一次来拿对象,此时需要创建对象给别人拿
instance = new SingleInstance2();
}
// c、将单例对象返回
return instance;
}
}
package com.app.d4_static_singleinstance;
/**
懒汉单例测试类
目标:掌握懒汉单例的设计;理解其思想
*/
public class SingleInstanceTest2 {
public static void main(String[] args) {
// 获取对象
SingleInstance2 s1 = SingleInstance2.getInstance();
SingleInstance2 s2 = SingleInstance2.getInstance();
System.out.println(s1 == s2);
// instance已经私有化,所以会报错!!
// SingleInstance2 s3 = SingleInstance2.instance;
}
}
true
Process finished with exit code 0
1、懒汉单例的实现步骤?
Java中提供了一个关键字:extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
public class Student extends People {
}
以上代码中,Student称为子类(派生类),People称为父类(基类或超类)。

作用: 当子类继承父类后,就可以直接使用父类公共的属性和方法了
package com.app.d5_extends;
/**
人类:父类
*/
public class People {
public void ports() {
System.out.println("父类公用方法被调用了!!");
}
}
package com.app.d5_extends;
/**
学生类:子类
使用extends继承了People类————父类
*/
public class Student extends People {
}
package com.app.d5_extends;
/**
继承测试类:
目标:认识继承这种关系;搞清楚使用继承的好处!
*/
public class ExtendsTest1 {
public static void main(String[] args) {
// 创建Student对象(子类),
Student s = new Student();
// 由于Student类 继承了 People类,所以儿子可以调用父亲的公用方法
s.ports();
}
}
控制台:
父类公用方法被调用了!!
Process finished with exit code 0
案例导学:请阅读以下代码存在的问题,并使用继承这个技术进行优化

可以看到,一个是学生类,一个是老师类;他们之间都有着相同的特征:成员变量、方法;
这样的话,重复代码又多了,是一种很不好的现象。
解决:继承(extends)关系;好处:提高代码的复用性。

1、什么是继承?继承有啥好处?
继承就是Java允许我们用extends关键字,让一个类与另一个类建立起一种父子关系;
好处就是减少代码冗余,提高了代码的复用性、增强类的功能扩展性。
2、继承的格式是什么样的?
3、继承后子类的特点是什么?
为什么?
案例:继承的设计规范
需求:
分析:
学生信息和行为(姓名,年龄,所在班级,查看课表,填写听课反馈);
老师信息和行为(姓名,年龄,部门名称,查看课表,发布问题);
定义角色类作为父类包含学生和老师相同的属性(姓名,年龄),行为(查看课表);
定义学生类(子类):包含属性(所在班级),行为(填写听课反馈);
定义老师类(子类):包含属性(部门名称),行为(发布问题)。
定义角色类,作为父类
package com.app.d6_extends_test;
/**
人类:父类
包含子类相同的属性和行为
*/
public class People {
/**
1、定义子类们相同的属性
*/
private String name; // 姓名
private int age; // 年龄
/**
2、提供属性对应的getter、setter方法,暴露其取值和赋值
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
3、定义子类们相同的行为方法:查看课表
*/
public void queryCourse() {
System.out.println(name + "查看了课表~~");
}
}
定义学生类,作为子类 继承 父类
package com.app.d6_extends_test;
/**
学生类:子类
继承父类,得到属性和行为
*/
public class Student extends People {
/**
1、定义子类自己独有的属性
*/
private String className; // 所在班级
/**
2、提供属性对应的getter、setter方法,暴露其取值和赋值
*/
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
/**
3、定义子类自己独有的行为方法:填写听课反馈
*/
public void writeInfo() {
System.out.println(className + "的" + getName() + "说: 老师666!!");
}
}
定义老师类,作为子类 继承 父类
package com.app.d6_extends_test;
/**
老师类:子类
继承父类,得到属性和行为
*/
public class Teacher extends People {
/**
1、定义子类自己独有的属性
*/
private String departmentName; // 部门名称
/**
2、提供属性对应的getter、setter方法,暴露其取值和赋值
*/
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
/**
3、定义子类自己独有的行为方法:发布问题
*/
public void problem() {
System.out.println(departmentName + "的" + getName() + "老师问: 这节课老师讲得好不好呀?");
}
}
定义测试类
package com.app.d6_extends_test;
/**
继承测试类
目标:掌握继承的设计规范,理解继承的设计思想。
*/
public class ExtendsTest1 {
public static void main(String[] args) {
// 1、创建学生对象
Student s1 = new Student();
// a、调用父类的行为方法:写入学生信息
s1.setName("孙悟空");
s1.setAge(799);
// b、调用子类自己独有的行为方法:写入所在班级
s1.setClassName("2022级天宫云雀9班");
// c、调用父类的行为方法:查看课表
s1.queryCourse();
System.out.println("-----------------------------");
// 2、创建老师对象
Teacher t1 = new Teacher();
// a、调用父类的行为方法:写入老师信息
t1.setName("唐僧");
t1.setAge(600);
// b、调用子类自己独有的行为方法:写入部门名称
t1.setDepartmentName("佛山老僧部");
// c、调用父类的行为方法:查看课表
t1.queryCourse();
System.out.println("-----------------------------");
// 3、老师提问
// a、调用子类自己独有的行为方法:发布问题
t1.problem();
// 4、学生回答问题
// a、调用子类自己独有的行为方法:填写反馈
s1.writeInfo();
}
}
控制台
孙悟空查看了课表~~
-----------------------------
唐僧查看了课表~~
-----------------------------
佛山老僧部的唐僧老师问: 这节课老师讲得好不好呀?
2022级天宫云雀9班的孙悟空说: 老师666!!
Process finished with exit code 0
1、继承需要满足什么样的设计规范?
子类可以继承父类的属性和行为,但是子类不能继承父类的构造器;
Java是单继承模式:一个类 只能继承一个直接父类;
就是你 只能有一个亲爸,不可能隔壁的老王叔、老杨叔 … 都是你的亲爸吧?
那从伦理角度看,肯定是不支持,但是从Java的角度呢?

为何不支持多继承,请看如下反证法:

Java不支持多继承,但是支持多层继承;
就是你 不可以继承多个爸爸;但是你可以继承你爸的,你爸可以继承你爷爷的,多层继承;
比如:子类A 继承父类B;父类B 可以继承父类 C。
Java中所有的类都是Object类的子类。
Java中所有的类,要么是直接继承了Object类,要么默认继承了Object类,要么间接继承了Object类;

所以说,Object是祖宗类。
1、子类是否可以继承父类的构造器?
2、子类是否可以继承父类的私有成员?
3、子类是否可以继承父类的静态成员?
package com.app.d7_extends_feature;
/**
继承测试类
目标:理解继承的特点
*/
public class ExtendsTest1 {
public static void main(String[] args) {
// 1、子类可以继承父类的属性和行为,但是子类不能继承父类的构造器;
// 2、子类是否可以继承父类的私有成员? 我认为是可以继承父类私有成员的,只是不能直接访问。
Tiger t1 = new Tiger();
// t1.eat();
// 3、子类是否可以继承父类的静态成员? 子类可以访问到父类的静态成员,但我认为这是共享,不算继承,共享并非继承。
System.out.println(Tiger.location);
}
}
/**
动物类:作为父类
*/
class Animal {
private void eat() {
System.out.println("动物要吃东西~~");
}
public static String location = "多多动物园";
}
/**
老虎类:作为子类继承父类
*/
class Tiger extends Animal {
}
1、继承有哪些特点?
如果子父类中,出现重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的成员怎么办?
可以通过 super 关键字,指定访问父类的成员。
格式:
super.父类成员变量/方法
package com.app.d8_extends_filed_method;
/**
继承测试类
目标:理解继承后成员的访问特点:就近原则
*/
public class ExtendsTest {
public static void main(String[] args) {
// 创建狗对象
Dog dog = new Dog();
dog.eat(); // 在子类中找不到此方法,在父类中找到了,所以是父类的
dog.lookDoor(); // 在子类中找到了,就近原则,所以是子类的
dog.showName(); // 在子类中找到了,就近原则,所以是子类的
//dog.go(); // 在子类中找不到,在父类中依旧找不到,无此成员,报错!!
}
}
/**
定义父类:动物类
*/
class Animal{
public String name = "动物名";
public void eat() {
System.out.println("动物要吃东西!!!");
}
public void run() {
System.out.println("动物可以跑~~~");
}
}
/**
定义子类:狗类 继承父类Animal
*/
class Dog extends Animal{
public String name = "狗名";
public void run() {
System.out.println("狗跑得贼快~~~");
}
public void lookDoor() {
System.out.println("狗可以看门~~~");
}
public void showName() {
String name = "局部名";
// 在子类局部范围中找到了name,就近原则,所以不会去子类成员范围和父类成员范围找
System.out.println(name);
// this.name: 代表找当前子类成员范围的name
System.out.println(this.name);
// super.name: 代表找父类成员范围的name
System.out.println(super.name);
run(); // 子类成员范围的run()
super.run(); // 父类成员范围的run()
}
}
控制台:
动物要吃东西!!!
狗可以看门~~~
局部名
狗名
动物名
狗跑得贼快~~~
动物可以跑~~~
Process finished with exit code 0
1、在子类方法中访问成员(成员变量、方法)需要满足什么?
2、如果子类和父类中出现了重复的成员,此时如果一定要在子类中使用父类的成员该如何?
方法重写的应用场景:
案例演示:
package com.app.d9_extends_override_method;
/**
继承测试类
目标:理解重写方法
*/
public class ExtendsTest {
public static void main(String[] args) {
// 创建手机对象
NewPhone hw = new NewPhone();
// 新手机功能
hw.callUp();
System.out.println();
hw.sendMessage();
}
}
/**
旧手机:父类
*/
class Phone{
public void callUp() {
System.out.println("打电话~~");
}
public void sendMessage() {
System.out.println("发短信~~");
}
}
/**
*/
class NewPhone extends Phone{
// 重写的方法
public void callUp() {
// 先用子类爸爸的功能
super.callUp();
// 在子类爸爸原本功能的基础上开发新功能
System.out.println("开始视频通话~~~");
}
// 重写的方法
public void sendMessage() {
// 先用子类爸爸的功能
super.sendMessage();
// 在子类爸爸原本功能的基础上开发新功能
System.out.println("发送语音~~~");
System.out.println("发送图片~~~");
}
}
打电话~~
开始视频通话~~~
发短信~~
发送语音~~~
发送图片~~~
Process finished with exit code 0
@Override是放在重写后的方法上,作为重写是否正确的校验注解。
加上该注解后如果重写错误,编译阶段会出现错误提示。
以后重写父类的方法都建议加上 @Override注解,代码可读性好、更安全、优雅。

重写方法的名称、形参列表必须与被重写方法的名称和形参列表一致;

私有方法不能被重写;

子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省 < protected < public)
语法了解一下,认识一下。
因为在实际开发中,子类重写的方法和父类方法是一模一样的,所以权限是等于父类的,不用乱改。
子类不能重写父类的静态方法,如果重写会报错的。

1、方法重写是什么样的?
2、方法重写建议加上哪个注解,有什么好处?
3、重写方法有哪些基本要求?
子类中所有的构造器默认都会访问父类中的无参数构造器,再执行自己。
package com.app.d10_extends_constructor;
/**
动物类:父类
*/
public class Animal {
public Animal() {
System.out.println("父类Animal无参数构造器被执行~~");
}
}
package com.app.d10_extends_constructor;
/**
狗类:子类
*/
public class Dog extends Animal{
public Dog() {
System.out.println("子类Dog无参数构造器被执行~~");
}
public Dog(String name) {
System.out.println("子类Dog有参数构造器被执行~~");
}
}
package com.app.d10_extends_constructor;
/**
继承测试类
目标:理解继承后,子类构造器的特点
特点:子类中所有的构造器默认都会先访问父类的无参数构造器后,再执行自己
*/
public class ExtendsTest1 {
public static void main(String[] args) {
Dog dog1 = new Dog();
System.out.println(dog1);
System.out.println("-----------");
Dog dog2 = new Dog("哈士奇");
System.out.println(dog2);
}
}
父类Animal无参数构造器被执行~~
子类Dog无参数构造器被执行~~
com.app.d10_extends_constructor.Dog@4eec7777
-----------
父类Animal无参数构造器被执行~~
子类Dog有参数构造器被执行~~
com.app.d10_extends_constructor.Dog@3b07d329
Process finished with exit code 0
为什么?
super(),不写也默认存在
1、子类继承父类后构造器的特点是什么样的?
初始化继承自父类的数据。
package com.app.d11_extends_constructor;
/**
人类:父类
*/
public class People {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.app.d11_extends_constructor;
/**
老师类:子类
*/
public class Teacher extends People {
public Teacher(String name, int age) {
// 调用父类的有参数构造器:初始化继承自父类的数据
super(name, age);
}
}
package com.app.d11_extends_constructor;
/**
继承测试类
目标:学习子类构造器如何去访问父类有参数构造器,还要清楚其作用!
作用:提高代码的复用性、编程更简洁
*/
public class ExtendsTest1 {
public static void main(String[] args) {
Teacher t1 = new Teacher("猪八戒", 34);
System.out.println(t1.getName());
System.out.println(t1.getAge());
}
}
猪八戒
34
Process finished with exit code 0
执行流程:

如果父类中没有无参构造器,只有有参构造器,会出现什么现象?
如何解决?
1、super调用父类构造器的作用是什么?
this代表本类对象的引用;super代表父类存储空间的标识。| 关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
|---|---|---|---|
| this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构造器 |
| super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造器 |
this调用本类其他构造器我们是没有接触过的。案例需求:
在学员信息登记系统中,后台创建对象封装数据的时候如果用户没有输入学校,则默认使用 “bilibili培训中心”。
如果用户输入了学校则使用输入的学校信息。
package com.app.d12_this;
/**
学生类
*/
public class Student {
private String name; // 学生姓名
private String schoolName; // 学校名称
public Student() {
}
/**
业务需求:当学生只输入姓名,不输入学校名称的时候,默认选择 "bilibili培训中心"
*/
public Student(String name) {
this(name, "bilibili培训中心");
}
public Student(String name, String schoolName) {
this.name = name;
this.schoolName = schoolName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
package com.app.d12_this;
/**
测试类
目标:理解this(...)的作用:本类构造器中访问其他构造器
*/
public class ThisTest1 {
public static void main(String[] args) {
Student s1 = new Student("殷素素", "冰火岛自学中心");
System.out.println(s1.getName());
System.out.println(s1.getSchoolName());
System.out.println("---------");
/**
如果学生不填写学校名称,默认这个学生填写的学校是:
bilibili培训中心。
*/
Student s2 = new Student("张无忌");
System.out.println(s2.getName());
System.out.println(s2.getSchoolName());
}
}
殷素素
冰火岛自学中心
---------
张无忌
bilibili培训中心
Process finished with exit code 0
super 去手动调用父类的构造器,最终还是会调用父类的构造器的。