什么是设计模式:
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
什么是单例模式:
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法;
- 单例模式有两种方式:1、饿汉式 2、懒汉式
单例模式步骤:
- 构造器私有化 –>防止用户直接new
- 类的内部创建对象
- 向外暴露出一个静态的公共方法,getInstance
- 代码实现
饿汉式:
class GirlFriend{
private String name;
//2、在类的内部直接创建对象
//为什么要用static修饰符?
//因为getInstance()是静态方法,静态方法只能访问静态属性
private static GirlFriend gf = new GirlFriend("小红红");
//1、将构造器私有化,防止直接new
private GirlFriend(String name){
this.name = name;
}
//3、提供一个公共的static方法,返回gf对象
private static GirlFriend getInstance(){
return gf;
}
}
单例模式中,对象通常是重量级的对象,饿汉式饿汉式可能造成创建了对象,但是没有使用。
懒汉式:
懒汉式,只有当用户使用getInstance()方法时,才返回Cat对象,后面再次调用时,会返回上次创建的cat对象,从而保证了单例
main方法中:
System.out.print(Cat.n1); //cat对象不会被创建
//n1是类变量,可以直接 类名.类变量名 访问
Cat instance = Cat.getInstance(); //此时Cat对象会被创建
System.out.print(instance);
//希望在程序进行过程中,只能创建一个Cat对象
//使用单例模式
class Cat{
private String name;
public static int n1 = 999;
//2、定义一个static静态属性对象
//跟饿汉式的区别是不会直接new
private static Cat cat ;
//步骤
//1、将构造器私有化
private Cat(String name){
this.name = name;
}
//3、提供一个public是static方法,可以返回一个Cat对象
public static Cat getInstance(){
if (cat == null) { //如果还没有创建cat对象
cat = new Cat("小可爱");
}
return cat;
}
}
饿汉式VS懒汉式:
- 两者主要的区别在于创建对象的时机不同:
- 饿汉式是在类加载就创建了对象实例
- 懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后续学习会讲到)
- 懒汉式创建对象的时候,如果同时传进来了多条指令,此时Cat对象还没有创建,那么就会同时执行多条”cat = new Cat(“小可爱”);”指令,单例模式就被破坏;
- 饿汉式存在了浪费资源的可能。
- 因为如果程序员一个实例对象都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
- 在javaSE标准类中,java.lang.Runtime就是经典的单例模式
- 当程序运行时,每个java应用程序都能得到一个运行时的实例,应用程序不能创建这个实例,只能从getRuntime()方法获得RunTime实例。
final基本使用
基本介绍:
final可以修饰类、属性、方法和局部变量在某些情况下,程序员可能有以下需求,就会使用到final:
1. 当不希望类被继承时,可以使用final修饰;
2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以使用final关键字修饰;
3. 当不希望类的某个属性值被修改,可以使用final修饰;
– 例:public final double TAX_RATE = 0.08;
4. 当不希望某个局部变量被修改,可以使用final修饰;
– 例:final double TAX_RATE = 0.08;
注意事项:
- final修饰的属性又叫常量,一般用 XX_XXX xx来命名;
- final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:(选择一个位置赋初值即可)
- 定义时:如 public fianl double TAX_RATE = 0.08;
- 在构造器中
- 在代码块中
- 如果final修饰的属性是静态的,则初始化的位置只能是:
- 定义时
- 在静态代码块 不能在构造器中赋值
- 因为构造器在创建对象的时候才会被触发,而静态变量的初始化在类加载的时候就要给值
- final类不能继承,但是可以实例化对象
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
- 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
- final不能修饰构造方法(即构造器)
- final 和 static 往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理
main方法:
System.out.print(Demo.i);
如果 public final int i = 16;
则输出
静态代码块被执行
16
如果 public static final int i = 16;
则输出
16
如果"public static final int i = 16;"没有加static,则类加载,静态代码块会被执行
class Demo{
public static final int i = 16;
static{
System.out.print("静态代码块被执行");
}
}
- 包装类(Integer,Double,Float,Boolean等都是final),String也是final类
抽象类:
引出
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
如:Animal类中有个eat方法,但是每种动物吃的东西都不一样,所以eat方法需要子类去重写
所谓抽象方法就是没有实现的方法,
所谓没有实现,就是指没有方法体
抽象类的介绍:
- 用abstract关键字来修饰一个类时,这个类就叫抽象类
- 访问修饰符 abstract 类名{}
- abstract class Animal{}
- 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
- 访问修饰符 abstract 返回类型 方法名(参数列表); //没有方法体
- public abstract void eat();
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
- 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多
abstract class Animal{
private String name;
public Animal(String name){
this.name = name;
}
/*
==当一个类中存在抽象方法时,需要将该类声明为abstract类
一般来说,抽象类会被继承,由其子类来实现抽象方法
*/
public abstract void eat();
}
注意事项:
- 抽象类不能被实例化
- 抽象类不一定要包含abstravt方法,也就是说,抽象类可以没有abstract方法
- 一旦类包含了abstract方法,则这个类必须声明为abstrac
- abstract只能修饰类和方法,不能修饰属性和其他的
- 抽象类可以有任意成员(因为抽象类还是类)
- 比如:非抽象方法、构造器、静态属性等等
- 抽象方法不能有主体,即不能实现
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法
- 除非它自己也声明为abstravt类
- 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的
abstract class H{
public abstract void hi(); //抽象方法
/*
如果是private修饰方法,private只有类本身可以访问,不对外公开,即便被继承了也没有机会重写hi方法
如果加final修饰方法,那么子类就不能重写该方法
static和abstract是一个非法的修饰符组合
*/
}
模板设计模式:
需求引出:
- 有多个类,完成不同的任务job
- 要求统计得到各自完成的时间
初始代码:
public class AA{
//计算任务:1+……+800000
public void job(){
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for(long i = 1;i<=800000;i++){
num += 1;
}
//得到结束的时间
long end = System.currentTimeMillis();
System.out.print("AA执行时间" + (end-start));
}
}
-----------------------------------------------------------------------------------------------------
public class BB{
//计算任务:1*……*8000
public void job(){
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for(long i = 1;i<=8000;i++){
num *= 1;
}
//得到结束的时间
long end = System.currentTimeMillis();
System.out.print("BB执行时间" + (end-start));
}
}
=======================================================
public class TestTemplate{
public static void main(String[] args) {
AA aa = new AA();
aa.job();
BB bb = new BB();
bb.job();
}
}
AA类和BB类有代码是重复的
AA类和BB类有代码是重复的
改进方法:
改进方法:
public class AA{
public void calculateTime(){
//得到开始的时间
long start = System.currentTimeMillis();
job();
//得到结束的时间
long end = System.currentTimeMillis();
System.out.print("AA执行时间" + (end-start));
}
//计算任务:1+……+800000
public void job(){
long num = 0;
for(long i = 1;i<=800000;i++){
num += 1;
}
}
}
------------------------------------------------------------------------------------------------------------------
public class BB{
public void calculateTime(){
//得到开始的时间
long start = System.currentTimeMillis();
job();
//得到结束的时间
long end = System.currentTimeMillis();
System.out.print("BB执行时间" + (end-start));
}
//计算任务:1*……*8000
public void job(){
long num = 0;
for(long i = 1;i<=8000;i++){
num *= 1;
}
}
}
============================================================
public class TestTemplate{
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
还是很麻烦~~引出抽象类方法~~
还是很麻烦:引出抽象类方法
抽象类方法:
抽象类方法:
abstract public calss Template{ //抽象类——模板设计模式
public abstract void job(); //抽象方法
public void calculateTime(){ //实现方法,调用了job()方法
//得到开始的时间
long start = System.currentTimeMillis();
job();
//得到结束的时间
long end = System.currentTimeMillis();
System.out.print("执行时间" + (end-start));
}
}
-------------------------------------------------------------------------------------------------------------------
public class AA extends Template{
//计算任务:1+……+800000
@Override
public void job(){ //实现Template的抽象方法job()
long num = 0;
for(long i = 1;i<=800000;i++){
num += 1;
}
}
}
-------------------------------------------------------------------------------------------------------------------
public class BB extends Template{
//计算任务:1*……*8000
@Override
public void job(){
long num = 0;
for(long i = 1;i<=8000;i++){
num *= 1;
}
}
}
============================================================
public class TestTemplate{
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
接口:
快速入门:
public interface UsbInterface{ //接口
//规定接口的相关规定
public void start();
public void stop();
}
--------------------------------------------------------------------------------------------------------------------------------
//Phone 类 实现 UsbInterface 接口
//即 Phone 类需要实现UsbInterface 接口 规定/声明的方法
public calss Phone implements UsbInterface{
@Override
public void start(){
System.out.print("手机开始工作~~~");
}
@Override
public void stop(){
System.out.print("手机停止 工作~~~");
}
}
--------------------------------------------------------------------------------------------------------------------------------
//Camera 类 实现 UsbInterface 接口
public class Camera implements UsbInterface{
@Override
public void start(){
System.out.print("手机开始工作~~~");
}
@Override
public void stop(){
System.out.print("手机停止工作~~~");
}
}
--------------------------------------------------------------------------------------------------------------------------------
public class Computer{
//编写一个方法,计算机工作
public void work(UsbInterface usbInterface){
//通过接口来调用方法
usbInterface.start();
usbInterface.stop();
}
}
==================================================================
public class TestInterface{
public static void main(String[] args) {
//创建手机,相机对象
Camera camera = new Camera();
Phone phone = new Phone();
//创建计算机
Computer computer = new Computer();
computer.work(phone); //把手机接入到计算机
/*
输出结果:
手机开始工作~~~
手机停止工作~~~
*/
computer.work(camera); //把相机接入到计算机
/*
输出结果:
相机开始工作~~~
相机结束工作~~~
*/
}
}
基本介绍:
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
语法:
interface 接口名{
//属性
//方法(1、抽象方法 2、默认实现方法 3、静态方法)
}
class 类名 implements 接口{
自己的属性
自己的方法
//但必须实现的接口的抽象方法
接口的抽象方法
}
小结:
- 在jdk7.0前,接口里所有的方法都没有方法体
- 在jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。需要使用default关键字修饰
注意事项:
- 接口不能被实例化
- 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
如:
void aaa();
实际上是abstract void aaa();
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口
- 接口中的属性,只能是final的,而且是public static final修饰符
比如
int a = 1;
实际上是 public static final int a = 1;(必须初始化)
--------------------------------------------------
interface IB{
int n1 = 10;
//private int n1 = 10 或 protected int n1 = 10 都不允许;
void hi();
}
public static void main(String[] args) {
System.out.print(IB.n1); //可以访问,说明就是static,
//静态属性可以用 类名.属性名访问
IB.n1 = 30; //修改不成功,说明n1是final
}
- 接口中属性的访问形式:接口名.属性名
- 一个接口不能继承其他的类,但是可以继承多个别的接口
例:
interface A extends B,C{ }
- 接口的修饰符,只能是 public 和 默认 ,这点和类的修饰符是一样的
接口VS继承:
- 接口和继承解决的问题不同:
- 继承的价值主要在于:解决代码的复用性和可维护性
- 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法
- 接口比继承更加灵活
- 继承是满足 is-a 的关系(如Cat->Animal),而接口只需满足 like-a 的关系
- 接口在一定程度上实现代码解耦
- 当子类继承了父类,就自动拥有了父类的能力
- 如果子类需要扩展功能,可以通过实现接口的方式来扩展
- 可以理解为 实现接口 是 对java 单继承机制的一种补充
例:
class Monkey{
private String name;
public Monkey(String name){
this.name = name;
}
public void climbing(){
System.out.print(name + "会爬树~~");
}
public String getName(){
return name;
}
}
//接口
interface Fishable{
void swimming();
}
interface Birdable{
void flying();
}
//继承
class LittleMonkey extends Monkey implements Fishable,Birdable{
public LittleMonkey(String name){
super(name);
}
@Override
public void swimming(){
System.out.print(getName() + "通过学习,可以像🐟一样游泳~~");
}
@Override
public void flying(){
System.out.print(getName() + "通过学习,可以像🐦一样飞~~");
}
}
main方法:
public static void main(String[] args) {
LittleMonkey wukong = new LittleMonkey();
wukong.climbing();
wukong.swimming();
wukong.flying();
}
接口多态特性:
- 多态参数
前面的Usb接口案例,Usb usb,既可以接收手机对象,又可以接收相机对象,体现了接口多态(接口引用可以指向实现了接口的类的对象) - 多态数组
> 案例:
> 给Usb数组中,存放 Phone 和 相机 对象,
> Phone类还有一个特有的方法call(),
> 请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,
> 还需调用Phone特有的方法call
interface Usb{
void woek();
}
class Phone_ implements Usb {
public void call(){
System.out.print("手机可以打电话~~");
}
@Override
public void work(){
System.out.print("手机工作中~~~");
}
}
class Camera_ implements Usb{
@Override
public void work(){
System.out.print("相机工作中~~~");
}
}
main方法:
public static void main(String[] args) {
//多态数组->接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_;
usbs[1] = new Camera_;
/*
给Usb数组中,存放 Phone 和 相机 对象,
Phone类还有一个特有的方法call(),
请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,
还需调用Phone特有的方法call
*/
for (int i = 0;i < usbs.length ;i++ ) {
usbs[i].work(); //动态绑定
//需要进行类型的向下转型
if (usbs[i] instanceof Phone_) { //instanceof 的作用是判断运行类型
//判断他的运行类型是Phone_
((Phone_) usbs[i]).call();
}
}
}
- 接口存在多态传递现象
interface IH { }
interface IG extends IH{ }
class Teacher implements IG{
}
--------------------------------------------------------------------------------------------------------------------------
public static void main(String[] args) {
//接口类型的变量可以指向 实现了该接口的类的对象实例
IG ig = new Teacher();
//如果 IG 继承了 IH 接口,而 Teacher类 实现了 IG 接口
//那么,实际上就相当于 Teacher 类也实现了 IH 接口
//这就是所谓的接口多态传递现象
IH ih = new Teacher();
/*
interface IH{}
报错,因为Teacher并没有实现IH的接口
interface IG extends IH{ }
不报错,
*/
}
接口和抽象类的区别:
- 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。
- 抽象类是对整个类整体进行抽象,包括属性、行为
- 但是接口却是对类局部(行为)进行抽象。
举个简单的例子
举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。
相同点
- 都不能被实例化。
- 接口的实现类和抽象类的子类只有全部实现了接口或者抽象类中的方法后才可以被实例化。
不同点
- 接口只能定义抽象方法不能实现方法,抽象类既可以定义抽象方法,也可以实现方法。
- 单继承,多实现。接口可以实现多个,只能继承一个抽象类。
- 接口强调的是功能,抽象类强调的是所属关系。
- 接口中的所有成员变量 为public static final, 静态不可修改,当然必须初始化。接口中的所有方法都是public abstract 公开抽象的。而且不能有构造方法。抽象类就比较自由了,和普通的类差不多,可以有抽象方法也可以没有,可以有正常的方法,也可以没有。