文章目录
面向对象的进阶
面向对象进阶的第一篇, 主要讲解内容如下:
- Static 关键字 : 开发中如何定义一个共享的信息,给所有对象共享访问,如在线人数信息等。
- 单例设计模式 : 有些类只需要一个对象就可以了,如任务管理器对象,如何实现一个类只能对外产生同一个对象
- 面向对象三大特征-继承 : 大量角色类的属性和行为存在重复代码,如果把一类的角色信息进行优化,提升代码复用,降低代码冗余
静态关键字:static
static修饰成员变量
static是静态的意思,可以修饰成员变量和成员方法。
static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。
public class User { | |
// 静态成员变量 | |
public static int onlineNumber=; | |
// 实例成员变量 | |
private String name; | |
private int age; | |
… | |
} |
成员变量可以分为2类:
- 静态成员变量
- 实例成员变量
静态成员变量
静态成员变量 : 有static修饰,属于类,与类加载时一起加载一次
常表示如在线人数信息、等需要被共享的信息,可以被共享访问。
public class User { | |
// 静态成员变量 | |
public static String onlineNumber=; | |
} |
静态成员变量 访问方式有两种:
通过类访问(推荐): 类名.静态成员变量
public static void main(String[] args) { | |
// 类名.静态成员变量 | |
System.out.println(User.onLineNumber); | |
} |
通过实例对象访问(能够访问但不推荐): 对象.静态成员变量
public static void main(String[] args) { | |
// 对象.静态成员变量 | |
User u = new User(); | |
System.out.println(u.onLineNumber); | |
} |
实例成员变量
实例成员变量 : 无static修饰,存在于每个对象中, 属于每一个实例对象
常表示姓名name、年龄age、等属于每个对象的信息。
public class User { | |
// 实例成员变量 | |
private String name; | |
private int age; | |
} |
实例成员变量的访问方式只有一种, 访问格式: 对象.实例成员变量
public static void main(String[] args) { | |
// 对象.实例成员变量 | |
User u = new User(); | |
u.name = "chenyq"; | |
u.age = 18; | |
System.out.println(u.name); | |
System.out.println(u.age); | |
} |
static修饰成员方法
刚刚我们学习了static修饰成员变量, 那修饰成员方法呢?
之前我们定义的方法有的有static修饰,有的是没有的,它们有什么不同
// 没有用static修饰 | |
public void run(){ | |
System.out.println(name +"正在好好学习,天天向上~~"); | |
} | |
// 有用static修饰 | |
public static int getMax(int a , int b){ | |
return a > b ? a : b; | |
} |
成员方法和成员变量相似, 也是分为两类:
- 静态成员方法
- 实例成员方法
实例成员方法 (有static修饰,属于类)
建议用类名访问(推荐),也可以用对象访问(同样不推荐)。
表示 对象自己的行为的 ,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
public class Student { | |
// 对象的成员变量 | |
private String name; | |
private Number age; | |
// 定义有参构造器 | |
public Student (String name, Number age) { | |
this.name = name; | |
this.age = age; | |
} | |
// 定义实例成员方法 | |
public void study() { | |
// 实例成员方法可以访问对象成员变量 | |
System.out.println(name + "正在学习"); | |
} | |
public static void main(String[] args) { | |
// 调用实例方法: 对象.实例方法 | |
Student stu = new Student("chenyq",); | |
stu.study(); // chenyq正在学习 | |
} | |
} |
静态成员方法 (无static修饰,属于对象)
只能用对象触发访问。
如果该方法是 以执行一个公用功能为目的 ,则可以申明成 静态方法 。
public class Student { | |
// 对象的成员变量 | |
private String name; | |
private Number age; | |
// 定义静态成员方法 | |
public static int getMax(int a, int b) { | |
if (a > b) return a; | |
return b; | |
} | |
public static void main(String[] args) { | |
// 调用静态方法: 类.静态方法 | |
// 同一个类中访问静态成员, 可以省略不写 | |
System.out.println(getMax(, 20)); // 省略 | |
System.out.println(Student.getMax(, 20)); | |
} | |
} |
静态方法定义工具类
工具类:
工具类中定义的都是一些静态方法,每个方法都是以完成一个共用的功能为目的。
现状问题分析:
在企业的管理系统中,通常需要在一个系统的很多业务处使用验证码进行防刷新等安全控制。
如果登录和注册等多处地方都存在验证码逻辑,就会导致同一个功能多处开发,会出现代码重复度过高。
工具类的好处:
一是 调用方便 ,二是 提高了代码复用 (一次编写,处处可用)
我们在工具类使用静态成员方法, 方便调用节约内存
实例方法需要创建对象调用,此时用对象只是为了调用方法,这样只会浪费内存。
工具类的定义注意:
建议将工具类的 构造器 进行私有,不允许创建对象。
里面都是静态方法,直接用类名访问即可, 工具类无需创建对象, 。
public class ArraysUtils { | |
// 工具类构造器私有化处理 | |
private ArraysUtils() { | |
} | |
} |
我们来试着定义一个数组工具类, 需求如下:
需求:在实际开发中,经常会遇到一些数组使用的工具类。请按照如下要求编写一个数组的工具类:ArraysUtils
思路分析:
我们知道数组对象直接输出的时候是输出对象的地址的,而项目中很多地方都需要返回数组的内容,请在ArraysUtils中提供一个工具类方法toString,用于返回整数数组的内容,返回的 字符串 格式如:[10, 20, 50, 34, 100]( 只考虑整数数组,且只考虑一维数组 )
public class ArraysUtils { | |
// 将构造器设为私有 | |
private ArraysUtils() { | |
} | |
public static String toString(int[] arr) { | |
String showArr = "["; | |
for (int i =; i < arr.length; i++) { | |
showArr += arr[i]; | |
showArr += (i == arr.length - ? "]" : ", "); | |
} | |
return showArr; | |
} | |
} |
经常需要统计平均值,平均值为去掉最低分和最高分后的分值,请提供这样一个工具方法getAerage,用于返回平均分。( 只考虑浮点型数组,且只考虑一维数组 )
public class ArraysUtils { | |
// 将构造器设为私有 | |
private ArraysUtils() { | |
} | |
public static String toString(int[] arr) { | |
String showArr = "["; | |
for (int i =; i < arr.length; i++) { | |
showArr += arr[i]; | |
showArr += (i == arr.length - ? "]" : ", "); | |
} | |
return showArr; | |
} | |
public static double getAerage(double[] arr) { | |
double max = arr[]; | |
double min = arr[]; | |
double sum =; | |
for (int i =; i < arr.length; i++) { | |
if (max < arr[i]) max = arr[i]; | |
if (min > arr[i]) min = arr[i]; | |
sum += arr[i]; | |
} | |
return (sum - max - min) / (arr.length -); | |
} | |
} |
定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。
定义好一个工具类, 在其他地方
public class TestDemo { | |
public static void main(String[] args) { | |
int[] nums = {, 20, 30, 40}; | |
// 调用 toString 方法, 并对返回结果打印 | |
System.out.println(ArraysUtils.toString(nums)); // [, 20, 30, 40] | |
double[] score = {.0, 1.8, 1.7, 1.9, 1.6}; | |
// 调用getAerage方法, 并对返回结果打印 | |
System.out.println(ArraysUtils.getAerage(score)); //.8 | |
} | |
} |
static访问注意事项:
静态方法只能访问静态的成员,不可以直接访问实例成员。
实例方法可以访问静态的成员,也可以访问实例成员。
静态方法中是不可以出现this关键字的。
Static定义代码块
静态代码块和构造代码块
代码块概述
代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。
在 Java 类下,使用 { } 括起来的代码被称为代码块 。
代码块分为 :
- 静态代码块
- 动态代码块
静态代码块:
格式: static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
public class TestDemo { | |
// 当类加载的时候, static中的代码块会自动执行一次 | |
static { | |
System.out.println("静态代码块与类一起加载的代码块, 自动触发一次"); | |
} | |
public static void main(String[] args) { | |
} | |
} |
构造代码块(了解,用的少):
格式: {}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
使用场景:初始化实例资源。
public class TestDemo { | |
{ | |
System.out.println("构造代码块与对象一起加载的代码块, 自动触发执行"); | |
} | |
// 定义构造器 | |
public TestDemo() { | |
System.out.println("构造器执行了"); | |
} | |
public static void main(String[] args) { | |
// 创建第一个对象 | |
new TestDemo(); | |
// 创建第二个对象 | |
new TestDemo(); | |
} | |
} |
打印结果如下:
我们发现, 构造代码块会比构造器先执行
由于构造代码块与对象一起加载, 索引 每创建一个对象, 构造代码块中的内容都会执行一次
静态代码块的小案例练习
相信大家都玩过斗地主游戏, 现在我们有如下一个需求
需求:
斗地主游戏在启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。
我们来完成这个提前准备好54张牌的功能
分析:
该房间只需要一副牌。
定义一个静态的ArrayList集合存储54张牌对象,静态的集合只会加载一份。
在启动游戏房间前,应该将54张牌初始化好
当系统启动的同时需要准备好54张牌数据,此时可以用静态代码块完成。
public class StaticCodeDemo { | |
// 定义静态成员变量 | |
public static ArrayList<String> cards = new ArrayList<>(); | |
// 使用静态代码块对静态资源cards进行初始化 | |
static { | |
String[] colors = { | |
"♥", "♣", "♠", "♦"}; | |
String[] nums = { | |
"", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2" | |
}; | |
for (int i =; i < nums.length; i++) { | |
for (int j =; j < colors.length; j++) { | |
cards.add(colors[j] + nums[i]); | |
} | |
} | |
// 添加大小王 | |
cards.add("大王"); | |
cards.add("小王"); | |
} | |
public static void main(String[] args) { | |
// 打印静态代码块查看效果 | |
System.out.println(cards); | |
} | |
} |
Static单例设计模式
參单例模式介绍
介绍 单例模式 之前, 我们先说一说什么是设计模式
设计模式(Design pattern):
开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的
这个最优的解法被人总结出来了,称之为设计模式。
设计模式有20多种,对应20多种软件开发中会遇到的问题,学设计模式主要是学2点:
- 第一:这种模式用来解决什么问题。
- 第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例模式:
可以保证系统中,应用该模式的这个类永远只有一个实例,即 一个类永远只能创建一个对象 。
例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
单例的实现方式有很多, 这篇我主要讲解以下两种单例模式:
饿汉单例模式。
懒汉单例模式。
饿汉单例模式
饿汉单例设计模式 : 在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
.定义一个类,把构造器私有, 让外界无法创建对象。 | |
.定义一个 静态变量 , 用于存储创建出来的对象 | |
//.定义一个单例类 | |
public class SingleInstance { | |
//.定义一个静态成员变量, 用于储存一个对象 | |
public static SingleInstance instance1 = new SingleInstance1(); | |
//.将类的构造器设置为私有构造器 | |
private SingleInstance() { | |
} | |
} |
我们在通过另一个类中, 多次通过 SingleInstance1.instance1 获取对象也只会获取一次对象
这是因为类和静态成员变量一起加载, 且只加载一次, 所以只会获取到一个对象
public class Test { | |
public static void main(String[] args) { | |
SingleInstance sin1 = SingleInstance1.instance1; | |
SingleInstance sin2 = SingleInstance1.instance1; | |
// 获取两次得到的是同一个对象 | |
System.out.println(sin == sin2); // true | |
} | |
} |
讀懒汉单例模式
懒汉单例设计模式 : 在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
.定义一个类,把构造器私有。 | |
.定义一个静态变量存储一个对象。 | |
.提供一个返回单例对象的方法 | |
public class SingleInstance { | |
//.定义一个静态变量, 用于存储一个对象, 但是不初始化对象 | |
public static SingleInstance instance2; | |
//.定义一个类, 把构造器私有 | |
public SingleInstance() { | |
} | |
//.提供一个返回单例对象的方法 | |
public static SingleInstance getObject() { | |
if (instance == null) return instance2 = new SingleInstance2(); | |
return instance; | |
} | |
} |
我们测试一下, 在另一个类中, 多次通过 SingleInstance1.getObject 获取对象, 是否只会获取一次对象
public class Test { | |
private static void main(String[] args) { | |
SingleInstance sin1 = SingleInstance2.getObject(); | |
SingleInstance sin2 = SingleInstance2.getObject(); | |
System.out.println(sin == sin2); // true | |
} | |
} |
饿汉单例模式和懒汉单例模式区别对比:
饿汉单例模式和懒汉单例模式各有优缺点
- 从第一次获取对象速度里来看: 饿汉单例模式的速度比懒汉单例模式快
- 从内存资源来看: 饿汉单例模式有可能会浪费资源, 而懒汉单例模式不会。