学习目标
java
能够说出单例设计模式的好处
//无论获取多少次对象,只产生一个对象
能够说出多例模式的好处
//可以产生固定的多个对象
扑克牌程序,一个"扑克类"会创建固定的54个对象,不能多、也不能少。
麻将程序,一个"骰子类"会创建固定的2个对象,不能多、也不能少。
程序中需要用到"颜色的表示",只能有三种颜色"红、绿、蓝",一个"颜色类(Color)"应该只创建三个对象,来代 表这三个颜色。
//多例模式的作用:使某个类,在程序运行期间,只能产生固定的几个对象,不能多、也不能少。
能够说出动态代理模式的作用
对对象进行代理,调用对象的功能
让一部分功能允许执行
让一部分功能不允许执行
可以自己定义一些代理的规则
能够使用Proxy的方法生成代理对象
Star wbqProxy = (Star)Proxy.newProxyInstance(WangBaoQiang.class.getClassLoader(),
WangBaoQiang.class.getInterfaces(),
new InvocationHandlerImpl(new WangBaoQiang()));
能够使用工厂模式编写java程序
public class AnimalFactory {
//定义一个生产动物的方法,参数传递动物的名称,根据名称创建指定的动物
public static Animal getInstance(String name){
if("cat".equals(name)){
return new Cat();
}else if("dog".equals(name)){
return new Dog();
}else {
//不是动物,返回null
return null;
}
}
}
一.单例设计模式(重点)
单例设计模式宗旨:保证一个类只产生一个对象
1.饿汉式
java
package com.itheima.demo01Singleton;
/*
单例:只能创建一个本类的对象
单例设计模式:饿汉式
形容人非常饥饿,直接把创建好的对象吃了
无论是否有人使用本类对象,先把本类对象创建创建好了,供用户使用
实现步骤:
1.私有空参数构造方法,不让用户直接创建对象
2.定义一个私有的静态Person变量,并进行初始化赋值(创建一个对象给变量赋值)
3.定义一个公共的静态方法,返回Person对象
*/
public class Person {
//1.私有空参数构造方法,不让用户直接创建对象
private Person() { }
//2.定义一个私有的静态Person变量,并进行初始化赋值(创建一个对象给变量赋值)
private static Person p = new Person();
//3.定义一个公共的静态方法,返回Person对象
public static Person getInstance(){
return p;
}
}
java
package com.itheima.demo01Singleton;
public class Demo01Singleton {
public static void main(String[] args) {
//正常情况下,客户根据类创建多个对象
//Person p1 = new Person();
//System.out.println(p1);//com.itheima.demo01Singleton.Person@4554617c
//Person p2 = new Person();
//System.out.println(p2);//com.itheima.demo01Singleton.Person@74a14482
//Person p3 = new Person();
//System.out.println(p3);//com.itheima.demo01Singleton.Person@1540e19d
//Person p4 = new Person();
//System.out.println(p4);//com.itheima.demo01Singleton.Person@677327b6
//测试单例设计模式,无论调用多少次静态方法,获取的对象都是同一个
for (int i = 0; i < 20; i++) {
Person p = Person.getInstance();
System.out.println(p);
}
}
}
2.懒汉式
java
package com.itheima.demo02Singleton;
/*
单例设计模式:懒汉式
人非常懒,抽一鞭子动一下
当我们要使用对象的时候,才会创建对象;一直不使用对象是不会创建的
实现步骤:
1.私有空参数构造方法,不让用户直接创建对象
2.在类中定义一个私有的静态的Peson变量,不进行初始化赋值
3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
*/
public class Person {
//1.私有空参数构造方法,不让用户直接创建对象
private Person(){}
//2.在类中定义一个私有的静态的Peson变量,不进行初始化赋值
private static Person p;
//3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
public static Person getInstance(){
//增加一个判断,判断变量p的值是否为null(是null第一次调用getInstance方法),创建对象
if(p==null){
p = new Person();
}
//变量p不是null直接返回
return p;
}
}
java
package com.itheima.demo02Singleton;
public class Demo01Singleton {
public static void main(String[] args) {
//测试单例设计模式,无论调用多少次方法,获取的都是同一个对象
for (int i = 0; i < 20; i++) {
Person p = Person.getInstance();
System.out.println(p);
}
}
}
3.懒汉式解决多线程安全问题
java
package com.itheima.demo03Singleton;
public class Demo01Singleton {
public static void main(String[] args) {
//创建两个线程,每个线程获取20次对象
new Thread(()->{
for (int i = 0; i < 20; i++) {
Person p = Person.getInstance();
System.out.println(p);
}
}).start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
Person p = Person.getInstance();
System.out.println(p);
}
}).start();
}
}
执行结果:
java
com.itheima.demo03Singleton.Person@47e3e4b5
com.itheima.demo03Singleton.Person@67c084e5
com.itheima.demo03Singleton.Person@67c084e5
com.itheima.demo03Singleton.Person@67c084e5
com.itheima.demo03Singleton.Person@67c084e5
com.itheima.demo03Singleton.Person@67c084e5
com.itheima.demo03Singleton.Person@67c084e5
...

java
package com.itheima.demo03Singleton;
public class Person {
//1.私有空参数构造方法,不让用户直接创建对象
private Person(){}
//2.在类中定义一个私有的静态的Peson变量,不进行初始化赋值
private static Person p;
//3.定义一个公共的静态的成员方法,返回Person对象,保证无论调用多少次方法,只返回一个对象
public static synchronized Person getInstance(){
//增加一个判断,判断变量p的值是否为null(是null第一次调用getInstance方法),创建对象
if(p==null){
p = new Person();
}
//变量p不是null直接返回
return p;
}
}
小结
单例模式可以保证系统中一个类只有一个对象实例。
实现单例模式的步骤:
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
二.多例设计模式(重点)
1.多例设计模式概述
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
例如:
扑克牌程序,一个"扑克类"会创建固定的54个对象,不能多、也不能少。
麻将程序,一个"骰子类"会创建固定的2个对象,不能多、也不能少。
程序中需要用到"颜色的表示",只能有三种颜色"红、绿、蓝",一个"颜色类(Color)"应该只创建三个对象,来代 表这三个颜色。
多例模式的作用:使某个类,在程序运行期间,只能产生固定的几个对象,不能多、也不能少。
2.获取多个通用的对象
java
package com.itheima.demo04Multiton;
import java.util.ArrayList;
import java.util.Random;
/*
多例设计模式:获取多个通用的对象
需求:
只让程序产生3个Student对象,不能多,也不能少
实现步骤:
1.私有空参数构造方法,不让用户直接创建对象
2.定义一个私有的,静态的,最终的变量,值定义创建对象的总个数
3.定义一个私有的,静态的ArrayList集合,用于存储多个对象
4.定义一个静态代码块(只执行一次),创建3个对象,存储到集合中
5.定义一个公共的静态方法,给用户随机在集合中取出一个对象返回
*/
public class Student {
//1.私有空参数构造方法,不让用户直接创建对象
private Student(){}
//2.定义一个私有的,静态的,最终的变量,值定义创建对象的总个数
private static final int MAX = 3;
//3.定义一个私有的,静态的ArrayList集合,用于存储多个对象
private static ArrayList<Student> list = new ArrayList<>();
//4.定义一个静态代码块(只执行一次),创建3个对象,存储到集合中
static {
for (int i = 0; i < MAX; i++) {
list.add(new Student());
}
}
//5.定义一个公共的静态方法,给用户随机在集合中取出一个对象返回
public static Student getInstance(){
//定义一个Random对象
Random r = new Random();
//在集合索引的范围内,随机产生一个随机数
int index = r.nextInt(list.size());//[0,1,2]
//通过随机的索引,在集合中取出Student对象返回
return list.get(index);
}
}
java
package com.itheima.demo04Multiton;
public class Demo01Multiton {
public static void main(String[] args) {
//测试多例设计模式,无论获取多少次对象,只会产生固定的3个
for (int i = 0; i < 20; i++) {
Student s = Student.getInstance();
System.out.println(s);
}
}
}
3.获取多个特定的对象
java
package com.itheima.demo05Multiton;
public class Student {
private String name;
private int age;
private String sex;
public Student() {
}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
java
package com.itheima.demo05Multiton;
public class Demo01Mulition {
public static void main(String[] args) {
//创建Student对象
Student s = new Student();
//给成员变量赋值
s.setName("张三");
s.setAge(18);
//s.setSex("男");
s.setSex("abc");//性别是一个字符串,随机的填写
System.out.println(s);
}
}
优化代码:解决用户随意填写性别问题
java
package com.itheima.demo06Multiton;
/*
多例设计模式:获取多个特定的对象
只产生两个Sex对象,一个代表男的性别,一个代表女的性别
实现步骤:
1.私有构造方法,不让用户直接创建对象
2.定义两个固定的对象(公共的,静态的,最终的),一个代表男的性别,一个代表女的性别
3.重写toString方法,打印对象的属性值(地址值)
*/
public class Sex {
private String s;
//1.私有构造方法,不让用户直接创建对象
private Sex(String s) {
this.s = s;
}
//2.定义两个固定的对象(公共的,静态的,最终的),一个代表男的性别,一个代表女的性别
public static final Sex MAN = new Sex("男");
public static final Sex WOMAN = new Sex("女");
//3.重写toString方法,打印对象的属性值(地址值)
@Override
public String toString() {
return s;
}
}
java
package com.itheima.demo06Multiton;
public class Student {
private String name;
private int age;
//使用Sex类型
private Sex sex;
public Student() {
}
public Student(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
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;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
}
java
package com.itheima.demo06Multiton;
public class Demo01Mulition {
public static void main(String[] args) {
//创建Student对象
Student s = new Student();
//给成员变量赋值
s.setName("张三");
s.setAge(18);
//s.setSex("男");
//s.setSex("abc");//性别的类型是Sex,不能随便给性别赋值,只能在Sex类中选择常量赋值
//s.setSex(Sex.MAN);
s.setSex(Sex.WOMAN);
System.out.println(s);
}
}
小结
多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.
实现多例模式的步骤:
- 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在类中定义该类被创建的总数量
- 在类中定义存放类实例的list集合
- 在类中提供静态代码块,在静态代码块中创建类的实例
- 提供获取类实例的静态方法
三.枚举(重点)
1.枚举的定义
java
package com.itheima.demo07enum;
/*
枚举:它就是"多例设计模式:获取多个特定的对象"的一种简化写法
需求:
只产生两个Sex对象,一个代表男的性别,一个代表女的性别
使用多例实现:
public static final Sex MAN = new Sex("男");
public static final Sex WOMAN = new Sex("女");
使用枚举实现:
MAN ==> public static final Sex MAN = new Sex();
WOMAN ==> public static final Sex MAN = new Sex();
注意:枚举中是有构造方法
---------------------------------------------------------------
枚举中有也可以定义成员变量,成员方法,构造方法(私有),这三个必须写在枚举常量的下边
MAN("男") ==> public static final Sex MAN = new Sex("男");
WOMAN("女") ==> public static final Sex WOMAN = new Sex("女");
*/
public enum Sex {
//MAN1,WOMAN1,
MAN("男"),WOMAN("女");
//定义成员变量
private String s;
//private Sex(){}
//添加带参数的构造方法
private Sex(String s) {
this.s = s;
}
//重写toString方法,返回对象中的字符串
@Override
public String toString() {
return s;
}
}
2.枚举的使用
java
package com.itheima.demo07enum;
public class Student {
private String name;
private int age;
private Sex sex;
public Student() {
}
public Student(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
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;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
}
java
package com.itheima.demo07enum;
public class Demo01Enum {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("迪丽热巴");
s1.setAge(18);
s1.setSex(Sex.WOMAN);
System.out.println(s1);
Student s2 = new Student("马尔扎哈",30,Sex.MAN);
System.out.println(s2);
}
}

3.枚举的应用
枚举的作用:枚举通常可以用于做信息的分类,如性别,方向,季度等。
枚举表示性别:
java
public enum Sex {
MAIL, FEMAIL;
}
枚举表示方向:
java
public enum Orientation {
UP, RIGHT, DOWN, LEFT;
}
枚举表示季度
java
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
小结
- 枚举类在第一行罗列若干个枚举对象。(多例)
- 第一行都是常量,存储的是枚举类的对象。
- 枚举是不能在外部创建对象的,枚举的构造器默认是私有的。
- 枚举通常用于做信息的标志和分类。
四.工厂设计模式(重点)
1.简单工厂设计模式
java
package com.itheima.demo08SimpleFactory;
public abstract class Animal {
public abstract void eat();
}
java
package com.itheima.demo08SimpleFactory;
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
java
package com.itheima.demo08SimpleFactory;
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉!");
}
}
java
package com.itheima.demo08SimpleFactory;
/*
定义一个生产动物的工厂
只能生产动物,不能生产其他对象
定义一个静态方法,根据用户传递的动物名称,创建指定的动物对象返回
*/
public class AnimalFactory {
public static Animal getInstance(String name){
if("cat".equals(name)){
return new Cat();
}else if("dog".equals(name)){
return new Dog();
}else{
//不是动物返回null
return null;
}
}
}
java
package com.itheima.demo08SimpleFactory;
/*
简单工厂设计模式:
创建一个工厂类,在工厂类中定义一个生产对象的方法
我们要使用对象,不在自己创建对象了,使用工厂类的方法获取
好处:
可以给工厂类的方法传递不同的动物名称,生产不同的动物对象
解除耦合性,增强了扩展性
弊端:
胡乱传递一个动物的名称,不存在,会抛出空指针异常
解决:
可以使用工厂方法设计模式来解决;创建多个工厂,每个工厂生产特定的动物
*/
public class Demo01SimpleFactory {
public static void main(String[] args) {
//使用工厂类的方法,获取指定的动物
//获取一个Cat对象
Animal cat = AnimalFactory.getInstance("cat");
cat.eat();
//获取一个Dog对象
Animal dog = AnimalFactory.getInstance("dog");
dog.eat();
//胡乱传递一个动物的名称
Animal car = AnimalFactory.getInstance("car");
//car.eat();//null.eat(); NullPointerException:空指针异常
}
}
2.工厂方法设计模式
java
package com.itheima.demo09FactoryMethod;
public abstract class Animal {
public abstract void eat();
}
java
package com.itheima.demo09FactoryMethod;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
java
package com.itheima.demo09FactoryMethod;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉!");
}
}
java
package com.itheima.demo09FactoryMethod;
/*
定义一个工厂接口:所有的工厂都的实现这个接口
*/
public interface Factory {
//定义一个生产动物的抽象方法
public abstract Animal getInstance();
}
java
package com.itheima.demo09FactoryMethod;
/*
生产猫的工厂,实现工厂接口
只生产猫
*/
public class CatFactory implements Factory{
@Override
public Animal getInstance() {
return new Cat();
}
}
java
package com.itheima.demo09FactoryMethod;
/*
生产狗的工厂,实现工厂接口
只生产狗对象
*/
public class DogFactory implements Factory{
@Override
public Animal getInstance() {
return new Dog();
}
}
java
package com.itheima.demo09FactoryMethod;
/*
工厂方法设计模式:
可以设计多个工厂,每个工厂生产不同的对象
猫工厂只生产猫,狗工厂只生产狗..
好处:
解决解决简单工厂设计模式的弊端,不让用户胡乱的传递参数了
弊端:
如果动物的种类过多,产生的工厂也有多个
*/
public class Demo01FactoryMethod {
public static void main(String[] args) {
//创建生产猫的工厂==>生产猫
CatFactory catFactory = new CatFactory();
Animal cat = catFactory.getInstance();
cat.eat();
//猫工厂可以生产多个猫
catFactory.getInstance().eat();
catFactory.getInstance().eat();
catFactory.getInstance().eat();
catFactory.getInstance().eat();
//创建生产狗的工厂==>生产狗
DogFactory dogFactory = new DogFactory();
Animal dog = dogFactory.getInstance();
dog.eat();
//狗工厂可以生产多个狗
dogFactory.getInstance().eat();
dogFactory.getInstance().eat();
dogFactory.getInstance().eat();
dogFactory.getInstance().eat();
dogFactory.getInstance().eat();
}
}
五.动态代理 (原理)
1.动态代理概述

2.动态代理代码实现
Star接口
java
package com.itheima.demo10proxy;
//培养明星的接口
public interface Star {
//教唱歌的方法
public abstract void changge();
//教跳舞的方法
public abstract void tiaowu();
//教演电影的方法
public abstract String yandianying(int money);
//教吃饭的方法
public abstract void chifan();
//教玩游戏的方法
public abstract void wanyouxi();
}
CaiXuKun类
java
package com.itheima.demo10proxy;
public class CaiXuKun implements Star {
@Override
public void changge() {
System.out.println("蔡徐坤在唱歌");
}
@Override
public void tiaowu() {
System.out.println("蔡徐坤在跳舞");
}
@Override
public String yandianying(int money) {
System.out.println("蔡徐坤在演电影:"+money);
return "电影演完了";
}
@Override
public void chifan() {
System.out.println("和蔡徐坤在一起吃饭");
}
@Override
public void wanyouxi() {
System.out.println("和蔡徐坤在一起玩游戏");
}
}
WuYiFan类
java
package com.itheima.demo10proxy;
/*
快捷键: ctrl+r 查找并替换
*/
public class WuYiFan implements Star {
@Override
public void changge() {
System.out.println("吴亦凡在唱歌");
}
@Override
public void tiaowu() {
System.out.println("吴亦凡在跳舞");
}
@Override
public String yandianying(int money) {
System.out.println("吴亦凡在演电影:"+money);
return "电影演完了";
}
@Override
public void chifan() {
System.out.println("和吴亦凡在一起吃饭");
}
@Override
public void wanyouxi() {
System.out.println("和吴亦凡在一起玩游戏");
}
}
InvocationHandler接口的实现类:
java
package com.itheima.demo10proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
java.lang.reflect.InvocationHandler接口
InvocationHandler 是代理实例的调用处理程序 实现的接口。
用来生产代理人对象的动态代理的接口
可以把被代理人(吴亦凡对象,蔡徐坤对象)传递到InvocationHandlerImpl类中,让InvocationHandler接口的实现生产明星的代理人对象
动态代理:传递哪个明星,就生产哪个明星的代理人对象
InvocatcionHandler接口中的方法:
Object invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。
作用:
使用invoke方法对被代理人(吴亦凡对象,蔡徐坤对象)的方法进行拦截,部分方法可以运行,部分方法不能运行
参数:
Object proxy:内部产生的代理人对象
Method method:invoke方法对明星的方法(changge,tiaowu...)进行拦截,方法内部使用反射技术获取到这些方法
Object[] args:拦截到明星的方法,方法的参数 changge() yandianying(int money)
返回值:
Object:就是拦截到方法的返回值
*/
public class InvocationHandlerImpl implements InvocationHandler{
//在实现类中定义一个Star类型的变量,用于接收被代理人对象(吴亦凡对象,蔡徐坤对象)
private Star star;
//使用构造方法,把明星传递到类中,给成员变量star赋值
public InvocationHandlerImpl(Star star) {
this.star = star;
}
/*
使用invoke方法对被代理人(吴亦凡对象,蔡徐坤对象)的方法进行拦截,部分方法可以运行,部分方法不能运行
调用明星的chagnge,tiaowu,yandianying的方法放行==>可以运行
调用明星的chifan,wanyouxi的方法拦截==>不让方法运行
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取到方法的名称
String methodName = method.getName();
//对方法名称进行判断
if("chifan".equals(methodName)){
throw new RuntimeException("不和你吃饭!");
}
if("wanyouxi".equals(methodName)){
throw new RuntimeException("不和你玩游戏!");
}
//其他的方法,使用Method对象中的方法invoke运行方法
Object v = method.invoke(star, args);
return v;
}
}
测试类:
java
package com.itheima.demo10proxy;
import java.lang.reflect.Proxy;
/*
动态代理:
创建代理人对象,对明星进行代理,对明星的方法进行拦截
调用明星的chagnge,tiaowu,yandianying的方法放行==>可以运行
调用明星的chifan,wanyouxi的方法拦截==>不让方法运行
想要实现动态代理:使用Proxy类生成代理人对象
java.lang.reflect.Proxy:
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
Proxy类中的静态方法:可以产生代理人对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
参数:
ClassLoader loader:传递类加载器
Class<?>[] interfaces:传递被代理人实现的所有接口 代理人(蔡徐坤,吴亦凡)实现的接口 Star.class
InvocationHandler h:生成代理人的接口,传递InvocationHandler接口的实现类对象
返回值:
Object:返回的就是创建的代理人对象
*/
public class Demo01Proxy {
public static void main(String[] args) {
//没有使用动态代理
CaiXuKun cxk = new CaiXuKun();
//cxk.chifan();
//cxk.tiaowu();
//cxk.chifan();
//使用Proxy类中的静态方法newProxyInstance获取蔡徐坤的代理人对象
Star cxkProxy = (Star) Proxy.newProxyInstance(CaiXuKun.class.getClassLoader(),
CaiXuKun.class.getInterfaces(),new InvocationHandlerImpl(cxk));
cxkProxy.changge();
cxkProxy.tiaowu();
String s = cxkProxy.yandianying(100);
System.out.println(s);
//cxkProxy.chifan();
//使用Proxy类中的静态方法newProxyInstance获取吴亦凡的代理人对象
Star wyfProxy = (Star)Proxy.newProxyInstance(WuYiFan.class.getClassLoader(),
WuYiFan.class.getInterfaces(),new InvocationHandlerImpl(new WuYiFan()));
wyfProxy.changge();
wyfProxy.tiaowu();
//wyfProxy.wanyouxi();
}
}

3.演示Java类似的动态代理方法
java
package com.itheima.demo11proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
java.util.Collections:操作集合的工具类
static <T> List<T> unmodifiableList(List<? extends T> list) 返回指定列表的不可修改视图。
此方法允许模块为用户提供对内部列表的"只读"访问。
在返回的列表上执行的查询操作将"读完"指定的列表。
试图修改返回的列表(不管是直接修改还是通过其迭代器进行修改)
将导致抛出 UnsupportedOperationException(运行时异常:不支持操作异常)。
unmodifiableList:模拟动态代理
传递List接口的实现类对象,方法内部对List接口的实现类对象进行代理
返回一个被代理后的List接口的实现类对象(相当于返回了代理人对象)
使用unmodifiableList方法代理了List接口之后
如果使用List接口的size,get方法,没有对集合的进行修改,就可以运行方法
如果使用List接口的add,remove,set方法,对象集合进行了修改,则会抛出UnsupportedOperationException异常
*/
public class Demo01Proxy {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//使用Collections中的方法unmodifiableList对List集合进行代理
List<String> listProxy = Collections.unmodifiableList(list);
//如果使用List接口的size,get方法,没有对集合的进行修改,就可以运行方法
System.out.println(listProxy.get(1));//b
System.out.println(listProxy.size());//3
//如果使用List接口的add,remove,set方法,对象集合进行了修改,则会抛出UnsupportedOperationException异常
//listProxy.add("d");//UnsupportedOperationException
//listProxy.remove(0);//UnsupportedOperationException
listProxy.set(2,"www");//UnsupportedOperationException
}
}
4.动态代理综合案例
需求:
使用动态代理模拟unmodifiableList方法,对List接口进行代理
调用List接口的方法会被拦截
如果使用的size,get方法,没有对集合进行修改,则允许执行
如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
分析:
1.定义一个代理方法proxyList
参数:传递List集合
返回值:被代理之后的List集合
2.方法内部可以使用Proxy类中的方法实现动态代理
代码实现:
java
package com.itheima.demo11proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
/*
需求:
使用动态代理模拟unmodifiableList方法,对List接口进行代理
调用List接口的方法会被拦截
如果使用的size,get方法,没有对集合进行修改,则允许执行
如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
分析:
1.定义一个代理方法proxyList
参数:传递List集合
返回值:被代理之后的List集合
2.方法内部可以使用Proxy类中的方法实现动态代理
*/
@SuppressWarnings("all")//注解的作用是抑制警告,不让警告出现
public class Demo02Proxy {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
List<String> proxyList = proxyList(list);
System.out.println(proxyList.size());//3
System.out.println(proxyList.get(1));//b
//proxyList.remove(0);//UnsupportedOperationException: remove no run!
//proxyList.add("d");//UnsupportedOperationException: add no run!
//proxyList.set(1,"www");//UnsupportedOperationException: set no run!
}
//1.定义一个代理方法proxyList
public static List<String> proxyList(List<String> list){
//2.方法内部可以使用Proxy类中的方法实现动态代理
List<String> listProxy = (List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(),
new InvocationHandler() {
//invoke方法获取List集合中的方法,对这些方法进行判断,让部分方法可以运行,让部分方法被拦截,抛出异常,不让方法运行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法的名称
String name = method.getName();
//如果使用的add,remove,set方法,对集合进行了修改,则抛出运行时异常
if("add".equals(name)){
throw new UnsupportedOperationException("add no run!");
}
if("remove".equals(name)){
throw new UnsupportedOperationException("remove no run!");
}
if("set".equals(name)){
throw new UnsupportedOperationException("set no run!");
}
//如果使用的size,get方法,没有对集合进行修改,则允许执行
Object v = method.invoke(list, args);
return v;
}
});
return listProxy;
}
}

动态代理案例流程图

5.总结
动态代理非常的灵活,可以为任意的接口实现类对象做代理
动态代理可以为被代理对象的所有接口的所有方法做代理,动态代理可以在不改变方法源码的情况下,实现对方法功能的增强,
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。
动态代理同时也提高了开发效率。
缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的。