1. 单例设计模式
1.1 设计模式
设计模式是一种代码模式,编程风格,以及解决问题的思考方式。
类似学生时期做题的“ 套路 ”,“ 答题模板 ”;
1.2 单例设计模式
单例指单个对象实例;
通过某种方法保证程序运行过程中,某个类只能存在且只提供某一个对象实例的方法;
1.首先必须将类的构造器的访问权限设置为 private,
这样,就不能使用 new 在类的外部任意创造类的对象;
2. 但在内部可以产生类的对象。
由于无法在外部得到该类的对象,
只能调用该类的某个静态方法以返回类内部创建的对象;
3. 静态方法只能访问类中的静态成员变量(静态方法只能访问类的静态方法和静态属性)。
因此,对于该类内部的对象的变量也须定义为静态的;
1.3 懒汉式
1.私有化类的构造器
2.内部声明类的静态对象:方便接下来调用,但不初始化
3.声明 public、static 的返回当前对象的方法
好处:延迟对象的创建(创建静态对象时赋值为 null,用时再创建)
坏处:以下内容存在线程不安全问题,需改善;
//懒汉式
class SignalMode2{
public static void main(String[] args) {
Order instance1 = Order.getInstance();
Order instance2 = Order.getInstance();
System.out.println(instance1 == instance2);//true
}
}
class Order{
//1.私有化类的构造器
private Order(){}
//2.内部声明类的静态对象:方便接下来调用,但不初始化
private static Order order = null;
//3.声明 public、static 的返回当前对象的方法
//每次调用该方法
public static Order getInstance(){
if(order == null){
order = new Order();
}
return order;
}
}
1.4 饿汉式
1.私有化类的构造器:避免类的外部任意创建对象
2.内部创建类的静态对象:方便接下来调用
3.提供静态方法,外部调用该方法返回该类的对象
好处:饿汉式线程安全;
坏处:对象加载时间过长(创建静态对象时便初始化,用时便直接拿过来)
public class SignalModel {
public static void main(String[] args) {
//调用静态方法,返回类的静态对象
Bank instance1 = Bank.getInstance();
Bank instance2 = Bank.getInstance();
//即使创建多个对象,也指向同一个地址值
System.out.println(instance1 == instance2);//true
}
}
//饿汉式单例设计模式
class Bank{
//1.私有化类的构造器:避免类的外部任意创建对象
private Bank(){};
//2.内部创建类的静态对象:方便接下来调用
private static Bank bank = new Bank();//习惯上使用 private
//3.提供静态方法,外部调用该方法返回该类的对象
public static Bank getInstance(){
return bank;
}
}
2. final
1. final 可以修饰类,方法,变量;
2. final 修饰类:此类不能被其它类继承
比如:String 类,System 类,StringBuffer 类
3. final 修饰方法,表明此方法不可以被重写
比如:Object 类中 getClass();
4. final 修饰变量。(属性是变量的一种)
4.1 修饰属性。可以考虑赋值的位置:显式初始化,代码块,构造器
4.2 修饰局部变量。
修饰形参时,表明形参是一个常量。调用此方法,给常量形参赋实参以后,便不能重新赋值
/**
* final:最终的
* 1. final 可以修饰类,方法,变量;
* 2. final 修饰类:此类不能被其它类继承
* 比如:String 类,System 类,StringBuffer 类
* 3. final 修饰方法,表明此方法不可以被重写
* 比如:Object 类中 getClass();
* 4. final 修饰变量。(属性是变量的一种)
* 4.1 修饰属性。可以考虑赋值的位置:显式初始化,代码块,构造器
* 4.2 修饰局部变量。
* 修饰形参时,表明形参是一个常量。调用此方法,给常量形参赋实参以后,便不能重新赋值
*
* static final:
* static:表示共有的;可修饰 属性,方法,代码块,内部类
* final:表示最终的;可修饰 类,方法,变量
* 故可修饰属性(多见):全局常量
* 方法(少见):自定义方法很少如此修饰
*
*/
public class FinalTest {
//修饰属性。可以考虑赋值的位置:显式初始化
final int AGE = 10;
// final int age;//报错
//修饰属性。可以考虑赋值的位置:代码块,否则会报错
final int A;
{
A = 100;
}
//修饰属性。可以考虑赋值的位置:构造器,否则会报错
final int C;
public FinalTest(){
C = 10;
}
// 修饰局部变量。
public void show(){
final int A = 100;
// A += 10;常量,不可以改变其值
}
public void getAdd(final int A){//形参 A 为常量,方法内不可以改变其值
System.out.println(A);
}
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
finalTest.getAdd(10);
}
}
3. abstract
3.1 引入
父类的某些方法需要引入,但不知如何定义,可以将其声明为抽象方法,该类必须为抽象类
作用:价值在于设计。设计者设计好后,让子类继承并实现
3.2 注意事项
1. 抽象类不能被实例化;
2. 抽象类不一定包含抽象方法,但抽象方法一定在抽象类中;
3. abstarct 只修饰类和方法
4. 抽象类可以有任意成员(本质还是类):非/抽象方法,构造器,静态属性等
5. 抽象方法不能有主体(包括花括号)
6. 若抽象类被普通类继承,则需重写所有抽象方法,被抽象类继承,则不需要重写方法
7. 抽象方法不能使用 private,final,static 修饰
private,私有方法外部不能调用
final,最终方法不允许重写
static,静态方法,为类所共用
public class AbstractClass {
public static void main(String[] args) {
//1.AbsTest是抽象的; 无法实例化
// new AbsTest();
}
}
abstract class AbsTest{
//2.抽象类不一定包含抽象方法,但抽象方法一定在抽象类中;
public abstract void show();
public void getAns(){
System.out.println("哈哈哈");
}
}
//6.若抽象类被普通类继承,则需重写所有抽象方法,被抽象类继承,则不需要重写方法
abstract class A{
//5.抽象方法不能有主体(包括花括号)
public abstract void Aa();
}
abstract class B extends A{
}
class C extends B{
@Override
public void Aa(){
System.out.println("结合绝");
}
}
3.3 抽象模板模式
抽象模板模式的使用:
计算完成后任务的时间体会抽象类/方法;
下面是第一版本;
/**
*抽象模板模式
*/
public class Sum {
public static void main(String[] args) {
Sum sum = new Sum();
sum.Cir();
SumA sumA = new SumA();
sumA.Cir();
}
public void Cir(){
//开始的时间
long start = System.currentTimeMillis();
int num = 0;
for (int i = 1; i <= 90000000; i++) {
num += 1;
}
//结束的时间
long end = System.currentTimeMillis();
//执行的时间
System.out.println("任务 A 执行的时间:" + (end - start) + "毫秒");
}
}
class SumA {
public void Cir(){
//开始的时间
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 9000000; i++) {
num += i * 2;
}
//结束的时间
long end = System.currentTimeMillis();
//执行的时间
System.out.println("任务 B 执行的时间:" + (end - start) + "毫秒");
}
}
下面是第二版本;
//但是上方代码计算任务执行时间重复性较高
//可如下简化,提高复用性
class GetSum{
public static void main(String[] args) {
//改进后,提高了代码的复用性
SumC sumC = new SumC();
sumC.Time();
SumD sumD = new SumD();
sumD.Time();
}
}
class SumC {
public void Time(){
//开始的时间
long start = System.currentTimeMillis();
Cir();//调用计算执行时间的任务即可
//结束的时间
long end = System.currentTimeMillis();
//执行的时间
System.out.println("任务 B 执行的时间:" + (end - start) + "毫秒");
}
public void Cir(){
long num = 0;
for (long i = 1; i <= 9000000; i++) {
num += i * 2;
}
}
}
class SumD {
public void Time(){
//开始的时间
long start = System.currentTimeMillis();
Cir();//调用计算执行时间的任务即可
//结束的时间
long end = System.currentTimeMillis();
//执行的时间
System.out.println("任务 A 执行的时间:" + (end - start) + "毫秒");
}
public void Cir(){
int num = 0;
for (int i = 1; i <= 90000000; i++) {
num += 1;
}
}
}
下面是 final 版本;
//上述每个类中都有一个 Time() ,仍然没有彻底提高代码的复用性
//继承抽象类,重写抽象方法,实现方法调用抽象方法,子类再调用实现方法
class Template{
public static void main(String[] args) {
SumE sumE = new SumE();
SumF sumF = new SumF();
sumE.Time();//继承了父类方法
sumF.Time();
}
}
//抽象类 - 模板设计模式
abstract class Tem{
//抽象方法
public abstract void Work();
//实现方法
public void Time(){
//开始的时间
long start = System.currentTimeMillis();
//调用计算执行时间的任务即可
Work();//动态绑定,子类会调用重写的方法
//结束的时间
long end = System.currentTimeMillis();
//执行的时间
System.out.println("任务执行的时间:" + (end - start) + "毫秒");
}
}
class SumE extends Tem {
@Override
public void Work(){//重写了抽象方法的抽象类
int num = 0;
for (int i = 1; i <= 90000000; i++) {
num += 1;
}
}
}
class SumF extends Tem{
@Override
public void Work(){//重写了抽象方法的抽象类
long num = 0;
for (long i = 1; i <= 9000000; i++) {
num += i * 2;
}
}
}
4. 接口
4.1 使用背景
举例:项目经理需要三位程序猿编写连接三种数据库的类。
若无接口进行规范,可能出现以下情况:
程序员 A :可能出现 f1 ( ),f2 ( ) 连接和关闭数据库;
程序员 B:可能出现 con ( ),close ( ) 连接和关闭数据库;
程序员 C :可能出现 connect( ),shutdown ( ) 连接和关闭数据库;
这产生了不必要的麻烦;
倘若写之前,项目经理提供连接数据库的抽象类,
并定义抽象方法 connection ( ),close ( ) 连接和关闭数据库,
程序员只管实现即可;
从中可体会到接口的用处
public class example {
public static void main(String[] args) {
MysqlDB mysqlDB = new MysqlDB();
show(mysqlDB);
}
public static void show(DBInterface dbInterface){// new MysqlDB();多态
dbInterface.connect();
dbInterface.close();
}
}
//接口:规范连接/关闭数据库的方法名
interface DBInterface{
void connect();//连接数据库
void close();//关闭数据库
}
//A 程序员
class MysqlDB implements DBInterface{
@Override
public void connect() {
System.out.println("连接 mysql");
}
@Override
public void close() {
System.out.println("关闭 mysql");
}
}
//B 程序员
class OracleDB implements DBInterface{
@Override
public void connect() {
System.out.println("连接 Oracle");
}
@Override
public void close() {
System.out.println("关闭 Oracle");
}
}
//C 程序员
class DB2 implements DBInterface{
@Override
public void connect() {
System.out.println("连接 DB2");
}
@Override
public void close() {
System.out.println("关闭 DB2");
}
}
4.2 接口定义
interface 接口名
{
1.属性
全局常量( 被 public sttaic final 修饰,且可省略)
2.方法
静态方法
默认方法
抽象方法(默认的,public static 可省略)
}
/**
* 接口定义:
* interface 接口名 {
* 1.属性
* 2.方法
* 静态方法
* 默认方法
* 抽象方法(默认的,public static 可省略)
*
* }
*/
public interface Interface02 {
//方法:抽象方法,可省略 public abstract
public void test();
//属性
public int age = 18;
// jdk8 之后,可以有默认实现方法
default void show(){
System.out.println("默认实现方法");
}
// jdk8 之后,可以有静态方法
public static void method(){
System.out.println();
}
}
4.3 注意事项
1. 接口不能被实例化
2. 接口中的 public 方法,默认是 abstract 方法,且都可被省略
3. 类实现接口,必须实现接口的所有抽象方法
4. 抽象类可以实现接口,且不用重写接口的方法
5. 一个类可以同时实现多个接口
6. 接口与接口是继承关系,接口和类是实现关系
/**
* 接口注意事项:
* 1.接口不能被实例化
* 2.接口中的 public 方法,默认是 abstract 方法,且都可被省略
* 3.方法实现接口,必须实现接口的所有抽象方法
* 4.抽象类可以实现接口
* 5.一个类可以同时实现多个接口
* 6.接口与接口是继承关系,接口和类是实现关系
*/
public class Warns {
}
//4.抽象类可以实现接口
interface Test{
void study();
void walk();
}
abstract class A implements Test{
}
4.4 多态特性
4.4.1 多态数组
//多态数组
class Interface{
public static void main(String[] args) {
//声明接口数组
Usb[] usbs = new Usb[2];
usbs[0] = new Camera_();
usbs[1] = new Phone_();
for (int i = 0; i < 2; i++) {
usbs[i].show();//接口的两个实现类都重写了该方法
//调用手机的特有方法,报错,需要向下转型
//usbs[i].call();
if(usbs[i] instanceof Phone_){//判断运行类型
((Phone_) usbs[i]).call();//自动转换为此样式
}
}
}
}
interface Usb{
void show();
}
class Camera_ implements Usb{
@Override
public void show() {
System.out.println("相机开机啦");
}
}
class Phone_ implements Usb{
@Override
public void show() {
System.out.println("手机开机啦");
}
//Phone 特有方法
public void call(){
System.out.println("手机打电话!");
}
}
4.4.2 多态传递
//多态传递
public class MultipleType {
public static void main(String[] args) {
//多态。子类引用指向父类变量
TestA testA = new student();
//接口多态的传递性
TestB testB = new student();
}
}
interface TestB{
void show();
}
interface TestA extends TestB{}
class student implements TestA{
@Override
public void show() {
}
}
4.5 练习题
//练习题
interface ExerciseA{
int x = 0;
}
class B{
int x = 1;
}
class C extends B implements ExerciseA{
public static void main(String[] args) {
new C().show();
}
int x = 100;
public void show(){
//指定不明确
//System.out.println(x);
// 实现的接口的 x
System.out.println(ExerciseA.x);//0
//父类的 x
System.out.println(super.x);//1
//当前对象的 x
System.out.println(this.x);//100
}
}
5. 接口 VS 继承
1. 子类继承父类,可以获得父类的方法和属性
若子类需要扩展功能,可以通过接口的方式扩展
2. 继承是为了提高代码复用性,降低代码冗余度
接口是为了设计各种规范,让其他类实现接口
3.接口比继承更加灵活,接口在一定程度上实现代码解耦
解耦:接口规范性 + 动态绑定
总结:实现接口是对 Java 单继承的一种补充
6. 接口 VS 抽象类
一句话:
接口是精简的抽象类,只有全局常量和方法,比抽象类少了构造器和成员属性
更能体现标准和规范的含义,
因此,总说,面向接口开发;
7. 内部类概述
外部类局部位置
局部内部类
匿名内部类(重点!!!)
外部类成员位置
成员内部类
静态内部类
/**
* 类的五大成员:
* 属性,方法,构造器,代码块,内部类
*
* 分类:
* 外部类局部位置
* 局部内部类
* 匿名内部类(重点!!!)
* 外部类成员位置
* 成员内部类
* 静态内部类
*
*/
public class InnerClass {
}
//外部类
class Outer{
private int i = 100;//属性
//构造器
public Outer(){};
public Outer(int i){
this.i = i;
};
public void Method(){//方法
System.out.println("Method()");
}
//代码块
{
System.out.println("代码块");
}
//内部类
class InnerClass{
}
}