1、单例模式(static实践)
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某
个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
步骤
- 构造器私有化=》防止直接new
- 类的内部创建对象
- 向外暴露一个静态的公共方法。
饿汉式和懒汉式区别
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
- 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题
- 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式
饿汉式
package zhh.chapter09.single;
/**
* 单例模式
* 饿汉式
* 用不用的着都创建此对象
*/
public class Single01 {
public static void main(String[] args) {
GirdFriend girdFriend1 = GirdFriend.getInstance();
System.out.println(girdFriend1); //GirdFriend{name='小红'}
GirdFriend girdFriend2 = GirdFriend.getInstance();
System.out.println(girdFriend2);//GirdFriend{name='小红'},可以看出来一样
System.out.println(girdFriend1==girdFriend2);//true
}
}
class GirdFriend{
//步骤[单例模式-饿汉式]
//1. 将构造器私有化
//2. 在类的内部直接创建对象(该对象是 static)
//3. 提供一个公共的 static 方法,返回 gf 对象
private String name;
private static GirdFriend girdFriend=new GirdFriend("小红");
private GirdFriend(String name) {
this.name = name;
}
public static GirdFriend getInstance(){
return girdFriend;
}
@Override
public String toString() {
return "GirdFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式
package zhh.chapter09.single;
/**
* 单例模式
* 懒汉式
* 用的时候再创建此对象
*/
public class Single02 {
public static void main(String[] args) {
Cat cat1 = Cat.getInstance();
System.out.println(cat1);//Cat{name='汤姆'}
Cat cat2 = Cat.getInstance();
System.out.println(cat2);//Cat{name='汤姆'}
System.out.println(cat1==cat2);//true
}
}
class Cat{
//1.仍然構造器私有化
//2.定義一個 static 靜態屬性對象
//3.提供一個 public 的 static 方法,可以返回一個 Cat 對象
//4.懶漢式,只有當用戶使用 getInstance 時,才返回 cat 對象,
// 後面再次調用時,會返回上次創建的 cat 對象,從而保證了單例
private String name;
private static Cat cat;
private Cat(String name) {
this.name = name;
}
public static Cat getInstance(){
if(cat==null){
cat=new Cat("汤姆");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
2、final关键字
final可以修饰类、属性、方法和局部变量.
使用场景
- 当不希望类被继承时,可以用final修饰.
- 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
- 当不希望类的的某个属性的值被修改,可以用final修饰.
- 当不希望某个局部变量被修改,可以使用final修饰
final修饰属性必须初始化,final修饰的普通属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一
- 定义时:如public final double TAX_RATE=0.08;
- 在构造器中
- 在代码块中
如果final修饰的属性是静态的,则初始化的位置只能是
- 定义时
- 在静态代码块,不能在构造器中赋值
注意事项
- final修饰的属性又叫常量,一般用XX_XX_XX来命名
- final类不能继承,但是可以实例化对象。
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
- 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(都没子类的何谈重写)
- final不能修饰构造方法(即构造器)
- 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
- final和static往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。
package zhh.chapter09;
public class ExerChapter09 {
public static void main(String[] args) {
System.out.println(A.a);//只输出1
}
}
class A {
public final static int a=1;
static{
System.out.println("类被加载!");
}
}
3、抽象类
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
介绍
- 用abstract关键字来修饰一个类时,这个类就叫抽象类: 访问修饰符 abstract 类名{}
- 用abstract关键字来修饰一个方法时,这个方法就是抽象方法: 访问修饰符 abstract 返回类型方法名(参数列表); //没有方法体{}
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
注意事项
- 抽象类不能被实例化
- 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
- 一旦类包含了abstract方法,则这个类必须声明为abstract
- abstract只能修饰类和方法,不能修饰属性和其它的
- 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
4、模板设计模式(实践抽象类)
基本介绍
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
模板设计模式能解决的问题
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式
实践
package zhh.chapter09.template;
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.timeCalculation(); //3
BB bb = new BB();
bb.timeCalculation(); //3
}
}
package zhh.chapter09.template;
abstract public class Template {
public abstract void job();
public void timeCalculation(){
long start = System.currentTimeMillis();
job(); //多态的动态绑定,当子类对象运行这个方法时,运行子类的job()
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
class AA extends Template{
@Override
public void job() {
long num=0;
for (int i = 0; i < 1000000; i++) {
num+=i;
}
}
}
class BB extends Template{
@Override
public void job() {
long num=0;
for (int i = 0; i < 1000000; i++) {
num+=i*i;
}
}
}
5、接口
接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【jdk7.0及以下】。接口体现了程序设计的多态和高内聚低偶合的设计思想。
特别说明:Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
注意事项
- 接口不能被实例化,但可以指向实现此接口的对象实例(多态)
- 接口中所有的方法是 public方法,接口中抽象方法,可以不用abstract修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现。可以使用 alt+insert 来解决
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口: class Pig implements IB,IC)
- 接口中的属性,只能是final的,而且是 public static final修饰符。比如:int a=1;实际上是public static final int a=1;(必须初始化)
- 接口中属性的访问形式:接口名.属性名
- 接口不能继承其它的类,但是可以继承多个别的接口:interface A extends B,CO
- 接口的修饰符只能是public 和默认,这点和类的修饰符是一样的。
接口和继承类的区别联系
接口和继承解决的问题不同
- 继承的价值主要在于:解决代码的复用性和可维护性。
- 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即更加的灵活..
接口比继承更加灵活
- 接口比继承更加灵活,继承是满足is - a的关系,
- 接口只需满足like - a的关系。接口在一定程度上实现代码解耦[即:接口规范性+动态绑定机制]
接口的多态
- 多态参数
- 多态数组
- 接口存在多态传递现象
package zhh.chapter09.interface_;
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态体现
//接口类型的变量 if01 可以指向 实现了 IF 接口类的对象实例
//类似于类的上下转型
IF if01 = new Monster();
if01 = new Car();
//继承体现的多态
//父类类型的变量 a 可以指向 继承 AAA 的子类的对象实例
//上下转型
AAA a = new BBB();
a = new CCC();
}
}
interface IF {}
class Monster implements IF{}
class Car implements IF{}
class AAA { }
class BBB extends AAA {}
class CCC extends AAA {}
package zhh.chapter09.interface_;
public class InterfacePolyArr {
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_) {//判断他的运行类型是 Phone_
((Phone_) usbs[i]).call();
}
}
}
}
interface Usb{
void work();
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
public void work() {
System.out.println("相机工作中...");
}
}
package zhh.chapter09.interface_;
/**
* 演示多态传递现象
*/
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果 IG 继承了 IH 接口,而 Teacher 类实现了 IG 接口
//那么,实际上就相当于 Teacher 类也实现了 IH 接口.
// 这就是所谓的 接口多态传递现象.
IH ih = new Teacher();
}
}
interface IH {
void hi();
}
interface IG extends IH{ }
class Teacher implements IG {
@Override
public void hi() {
}
}
6、内部类
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类]】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
四种内部类
- 定义在局部位置(方法中/代码块):(1)局部内部类(2)匿名内部类
- 定义在成员位置1)成员内部类(2)静态内部类
语法
class outer {//外部类
class inner{}//内部类
}
class other{}//外部其他类
局部内部类
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量(本质还是一个类)。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码块中。
- 局部内部类---访问---->外部类的成员【访问方式:直接访问】
- 外部类---访问---->局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
- 外部其他类---不能访问----->局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package zhh.chapter09.innerclass;
/**
* 局部内部类的使用
* 定义在外部类的局部位置,比如方法中,并且有类名。
*/
public class LocalInnerClass {
public static void main(String[] args) {
new localouter1().outsum();
//输出为:
// 在外部类访问内部类成员
// 内部类num1: 200
// 外部类num1: 100
}
}
class localouter1{
private int num1=100;
public void outsum(){
//2、不能添加访问修饰符,因为它的地位就是一个局部变量(本质还是一个类)。
// 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
// 不会被另一个内部类继承
final class inner1{
private int num1=200;
public void insum(){
//1、可以直接访问外部类的所有成员,包含私有的
//4、局部内部类---访问---->外部类的成员【访问方式:直接访问】
//6、如果外部类和局部内部类的成员重名时,默认遵循就近原则,
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
//7、localouter1.this 本质就是外部类的对象
System.out.println("内部类num1: "+num1+"\n外部类num1: "+localouter1.this.num1);
}
}
//5、外部类---访问---->局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
inner1 inner1 = new inner1();
System.out.println("在外部类访问内部类成员");
inner1.insum();
}
public void outf(){
//下边报错,
//3、作用域:仅仅在定义它的方法或代码块中。
//insum();
}
}