文章目录
- 1.Tcp单向通讯:" ".getBytes()
- 2.Tcp双向通讯:socket中介
- 3.案例_文件上传:ServerSocket
- 4.多线程优化服务端与UUID:is和fos
- 5.Junit测试框架:手动加jar包
- 6.Class对象三种获取方式:Class.forName(" ");
- 7.反射:clazz.newInstance()
- 8.注解与元注解:12种类型
- 9.自定义junit测试框架并打jar包:method.isAnnotationPresent
- 10.类加载器:.class.getClassLoader(),引扩应,双亲委派
- 11.代理租房:静态代理,中介可代理实现FD接口的多个房东。装饰者设计模式(IO流,可层层增加功能),迭代器设计模式(集合)
- 12.动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
- 13.模板:类里有抽象方法必须抽象类
- 14.单例:某个类只能有唯一的一个实例对象,多线程
- 15.工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()
- 16.策略+工厂:去除if-else
- 17.DTD约束:配置文件
- 18.schema约束:命名空间
- 19.xml解析:dom4j
- 20.javase总结:IO流,集合
- 21.求一个长度为10的整型数组的最大元素,采用随机的方式赋值并输出各元素的值?
1.Tcp单向通讯:" ".getBytes()
端游将页游
好处和坏处相反。一个字节是2的8次方即256,所以0-255
。ipv4组合方式共2的32(4*8=32)次方即43亿个。
ip相当于小区,port相当于门牌号
。
下面将三要素融合,https中s就是secure。
socket封装了io流,所以要关。高层socket关了,低层也会关。如下accept会阻塞。
package com.itheima01.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1"; //1.连接服务端(指定服务端的ip和port)自动连接
int port = 10010;
Socket socket = new Socket(host, port);
OutputStream os = socket.getOutputStream(); //2.写出数据
os.write(" hello, tcp".getBytes());
socket.close();
}
}
package com.itheima01.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port。ip是路由器分配的
System.out.println("1");
Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
System.out.println("2");
InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
String msg = new String(buffer, 0, length);
System.out.println("收到客户端的数据:" + msg);
}
socket.close();//关闭和客户端的连接
serverSocket.close(); //关闭服务端
}
}
2.Tcp双向通讯:socket中介
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
//准备,写,读
public class TcpClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1"; //1.连接服务端 (指定服务端的ip和port) 自动连接
int port = 10010;
Socket socket = new Socket(host, port);
//11111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //2.写出数据
os.write(" hello, tcp".getBytes());
socket.shutdownOutput(); //解决: 告知对方 写完了。没有关闭socket,关闭就连接不了
//1111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream(); //3.客户端收到数据
int length=-1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
String msg = new String(buffer, 0, length);
System.out.println("收到服务端的数据:" + msg);
}
socket.close();
}
}
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port
System.out.println("1");
Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
System.out.println("2");
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
int length = -1;
byte[] buffer = new byte[1024];
/*
* 问题: 如下read方法是一个阻塞方法
* 以前: 读文件,读到末尾返回-1
* 现在: 读socket中传输的数据 (像电话), 对方socket关掉就算是末尾了
* 解决: 1. 对方在输出结束,可以直接关闭socket,这个数据就结束了,但是对方就不能再接收了
* 2. 对方直接告知已经输出结束,socket不需要关
*/
while((length = is.read(buffer)) != -1){//客户端socket.shutdownOutput();服务端就不会阻塞在这一行
String msg = new String(buffer, 0, length);
System.out.println("收到客户端的数据:" + msg);
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //4.服务端写出数据
os.write(" 好,退下吧~~".getBytes());
socket.close();//关闭和客户端的连接 //这里socket.shutdownOutput();可以不写,因为直接close了
serverSocket.close(); // 关闭服务端
}
}
3.案例_文件上传:ServerSocket
如下上面是服务端。
package com.itheima03.upload;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
FileInputStream fis = new FileInputStream("柳岩.jpg");//读本地文件(da14模块下有个柳岩.jpg),写到服务器socket中
OutputStream os = socket.getOutputStream();
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){ //读
os.write(buffer,0,length); //写
}
fis.close();
socket.close();
}
}
package com.itheima03.upload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086);
Socket socket = serverSocket.accept();
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream();
long time = System.currentTimeMillis();//系统当前时间毫秒值
FileOutputStream fos = new FileOutputStream("c:/test/server/" + time + ".jpg");
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){ //读
fos.write(buffer,0,length); //写
}
fos.close();
socket.close();
serverSocket.close();
}
}
4.多线程优化服务端与UUID:is和fos
package com.itheima04.good;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/*
* 服务器一般是24小时不关闭的,前面写的只能收到一张图片服务器就关闭了。解决方案:
* 1. 死循环
* java.net.BindException: Address already in use: JVM_Bind。绑定异常(port重复占用)
*
* 2. 想支持多个客户端的并发访问
* 解决: 引入多线程 代码加在阻塞之后
* 补充: 线程池 管理 线程(1. 避免线程频繁创建和销毁 2. 控制线程数量)
*
* 2. 文件名 : 以系统当前时间命名(并发: 同一毫秒,有两个用户同时上传文件, 就会有用户文件覆盖)
* 1. 一般文件,不需要标识这个图片属于谁的 : 服务器用UUID
* 2. 文件名需要跟用户上传的文件名一致 : 需要另一个线程传输这个文件名(这个用户id + 文件名)
*/
public class UploadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086); //只创建一个服务端,一个端口不能被两个程序占用,不能在这行上面加while(true)
while(true){ //死循环接收客户端请求
Socket socket = serverSocket.accept(); //虽然while(true),但这行会阻塞,单线程
//有客户上门才开线程,不能无限开。
new Thread(new Runnable() { //不能放accept()前面,不能无限开线程
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
// long time = System.currentTimeMillis();//系统当前时间毫秒值
String name = UUID.randomUUID().toString().replace("-", ""); //下行time改为name
FileOutputStream fos = new FileOutputStream("c:/test/server/" + name + ".jpg");
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
fos.write(buffer,0,length);
}
fos.close(); //应该放在finally中
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
// serverSocket.close(); // 服务器不能关闭,关了就不回到while(true)了
}
}
}
package com.itheima04.good;
import java.util.UUID;
// UUID: 通用唯一标识符(Universally Unique Identifier),永不重复
public class UuidDemo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// UUID uuid = UUID.randomUUID();
// String str = uuid.toString().replace("-", "");
String str = UUID.randomUUID().toString().replace("-", ""); //这行同上两行
System.out.println(str);
}
}
}
如下未把UUID的 - 替换为空。
如下BS案例:socket被写,页面被读。写一个浏览器程序难度和操作系统一样,鸿蒙os就是浏览器。
package com.itheima05.bs;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BsServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10090);
Socket socket = serverSocket.accept(); //接收浏览器请求
//1111111111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //写出页面
os.write("HTTP/1.1 200 OK\r\n".getBytes()); // http协议的响应行 第一行
os.write("Content-Type:text/html\r\n\r\n".getBytes()); //http协议的响应头 第二行,第三行为空白
FileInputStream fis = new FileInputStream("index.html"); //已有,用来被读
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
os.write(buffer,0,length); //第四行,响应体
}
fis.close();
socket.close();
serverSocket.close();
}
}
5.Junit测试框架:手动加jar包
package com.itheima01.junit;
import org.junit.Test;
/*
* 使用Junit测试框架:1. 写一个方法,在方法声明上加上 注解 @Test
* 2. 导库 : jar包 (全称java archive : java档案包 ) -> 很多.class文件的压缩包
* JDK : JVM + 很多jar包(这样开发者才能使用sun公司的很多类)
* idea开发工具自动集成了 junit测试框架的jar包
*/
public class JunitDemo {
/* public static void main(String[] args) { //不用再写main方法了
int i = 1/0;
System.out.println(i);
}*/
@Test
public void method01(){
int i = 1/0;
System.out.println(i); //出现异常,jvm要创建异常,打印异常信息io耗时
}
}
如下必须要文件类名和public class类名保持一致,一个类即一个文件。
如上产生如下jar包。
如上若失效解决:在工程目录下建一个与src并列的包test,并且右击test文件夹Mark Director as为Test Sources Root。File -> Project Structure->Modules再如下:
package com.itheima01.junit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
/*
* Junit测试框架的使用规范
* 1. 写一个方法,在方法声明上加上 注解 @Test (如果当前项目没有集成Junit jar包, 需集成)
* 2. 方法声明
* public void 方法名(空){ //里面必须为空,不能有参数
* 要测试的代码
* }
* 3. 使用:1. 把鼠标放在方法内, 可以使当前方法运行
* 2. 鼠标放在类中方法外, 可以使类中所有测试方法运行
* 4. 注解上:1. @Test : 核心注解
* 2. @Before : 在每一个@Test注解之前都会运行(用在资源初始化上,可以少写代码)
* 3. @After : 在每一个@Test注解之后都会运行(用在资源释放上)
*/
public class JunitDemo02 {
FileInputStream fis;
@Before
public void method00(){
System.out.println("在每个Test之前"); //注意 每个
}
@After
public void method11(){
System.out.println("在每个Test之后");
}
//1111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01(){
System.out.println("xx");
}
@Test
public void method02(){
System.out.println("yy");
}
}
6.Class对象三种获取方式:Class.forName(" ");
package com.itheima02.clazz;
import org.junit.Test;
/*
* 反射前提: Class对象
* 1. 引用类型: Class(大写C和关键字class不一样)
* 2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
* 3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
* .class文件(硬盘) -> Class对象(内存)
* 4. Class对象的特点:
* 1. Class对象的创建:
* 一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
*
* 重点: Class对象(类对象)是JVM创建的, 开发者是无法创建的。(开发者可以new一个object,这叫实例对象)
* 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
*
* 2. 三种Class对象的获取(不是创建)
* 1. 类名.class
* 2. 对象名.getClass()
* 3. Class.forName(全限定名); -> 加载配置文件
*/
public class ClassDemo {
@Test
public void method01(){
Class<Student> clazz = Student.class;
System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
synchronized (ClassDemo.class){ //当前类名.class -> 锁对象
//这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
//ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
}
}
@Test
public void method02(){
Student s = new Student();
Class<?> clazz = s.getClass();
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
System.out.println(clazz == Student.class); //true
}
@Test
public void method03() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
}
}
package com.itheima02.clazz;
import java.util.Objects;
public class Student {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true; //this表示student类
// this.getClass() == o.getClass()
// o instanceof Student
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
7.反射:clazz.newInstance()
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 反射(reflect) : 在程序运行时操作类
* 1. 运行时 : .java源码 -> .class字节码(编译) -> runtime(运行)
* 2. 操作 :
* 3. 类 : .java -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
* 1. 构造方法 / 构造器 Constructor
* 2. 方法 Method
* 3. 属性 Field
* A. 操作构造方法: 以前: 用构造方法来 创建对象
*/
public class ConstructorDemo {
public static void main(String[] args) {
// Person p = new Person(); //Person{name='null',age=0}
Person p = new Person("张三");
System.out.println(p); //Person{name='张三',age=0}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 反射 : 操作构造方法
//1. 获取Class对象
//2. 再获取Class对象中的构造方法
//3. 使用构造方法创建对象
Class clazz = Person.class;
/*
* Constructor<T> getConstructor(Class<?>... parameterTypes)
* 1. 参数: 需要指定想要获取的构造方法的参数列表 类型
* 2. 返回值: 返回构造方法的对象
*/
//Constructor constructor = clazz.getConstructor(String.class,int.class);
Constructor constructor = clazz.getConstructor(String.class);
/*
* T newInstance(Object ... initargs)
* 1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
* 2. 返回值: 返回创建好的实例
*/
Object obj = constructor.newInstance("张三");
System.out.println(obj); //Person{name='张三',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //简易的api
Class clazz = Person.class;
/* Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj); //Person{name='null',age=0} */
//Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
Object obj = clazz.newInstance();
System.out.println(obj); //Person{name='null',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //暴力反射: 知道(可以访问private方法)
Class clazz = Person.class;
/*
* getConstructor() : 只能获取public修饰的构造
* getDeclaredConstructor() : 获取所有权限的构造(private也可以)
*/
// Constructor constructor = clazz.getConstructor(String.class, int.class);
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用,所以加这行可以调用
Object obj = constructor.newInstance("李四", 18);
System.out.println(obj); //Person{name='李四',age=18}
}
}
package com.itheima03.reflect;
public class Person {
public String name;
private int age;
public Person(){
}
public Person(String name){
this.name = name;
}
private Person(String name,int age){ //注意private,用暴力反射
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void speak(String msg){
System.out.println("speak:" + msg);
// return 1;
}
public void speak(double msg){
System.out.println("speak:" + msg);
}
private void show(int year){
System.out.println("show:" + year);
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
public static void main(String[] args) {
Person p = new Person();
// Person p = null; //speak方法设为静态,不报错
p.speak("呵呵");
System.out.println(number); //speak:呵呵
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1. 获取Class对象
//2. 获取方法 .getMethod
//3. 调用方法 .invoke
Person p = new Person();
Class<?> clazz = p.getClass();
/*
* Method getMethod(String name, Class<?>... parameterTypes)
* 1 .参数
* 1. name 方法名
* 2. name 方法名的参数列表
* 2. 返回值: 获取的这个方法
*/
Method method = clazz.getMethod("speak", String.class);
/*
* Object invoke(Object obj, Object... args)
* 1. 参数
* 1. obj : 调动此方法的对象,speak前加一个static,null也可以
* 2. args : 调用此方法传入的实参
* 2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
* */
Object result = method.invoke(p, "嘻嘻");
//Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
System.out.println(result); //speak:嘻嘻
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person p = new Person();
Class<? extends Person> clazz = p.getClass();
Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射
show.setAccessible(true); //临时修改权限
show.invoke(p,15); //show:15
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
public static void main(String[] args) {
Person p = new Person();
p.name = "啦啦";
System.out.println(p); //Person{name='啦啦',age=0}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test //下面写那么多代码为了实现上面3行
public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//1.获取Class对象
//2.获取属性对象
//3.操作属性,get / set
Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
Object p = clazz.newInstance();
/*
* Field getField(String name)
* 1. 参数: 属性名
* 2. 返回值: 通过属性名找到的属性对象
*/
Field age = clazz.getDeclaredField("age");
age.setAccessible(true);
/*
* set(Object obj, Object value)
* 1. 设置属性的对象
* 2. 设置的属性值
*/
age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
System.out.println(p);//Person{name='null',age=18}
}
}
8.注解与元注解:12种类型
package com.itheima04.annotation;
import java.util.Date;
/*
* 注解: annotation ( 注释;注解,java 四大类型)
* 1. 注释: 一般是给人看的代码解释性说明 (不参与编译, 运行)
* 注解: 给JVM看的代码 解释性说明(可能参与编译或运行)
* 2. 常见
* 1. @Override : 检测此方法是否是重写方法
* 2. @FunctionalInterface:此接口是否是函数式接口
* 3. @Deprecated : 加上方法上,声明此方法过时了(这个方法有更好的替代方案),但是还能用
*/
public class Demo01 {
public static void main(String[] args) {
new MyRunnable().run();
}
}
class MyRunnable implements Runnable{
@Deprecated
@Override
public void run() {
System.out.println("---------");
}
}
package com.itheima04.annotation;
import java.util.Date;
/*
* 注解的基本语法
* 1. 关键字: @interface
* 2. 属性: String name() default "yy"; (注解属性类型: 8大基本类型和String,枚举,注解,Class 。 以及这12种类型的数组。不包括Date等)
* 3. 注解没有方法
*
* 注解的使用: 可以在代码上加注解
* 定义@Override :
* 1. 适用范围 : 方法上
* 2. 生命周期(从创建到销毁):
* SOURCE : 源码 (@Override写在这个时候,没必要到下面两个阶段,节省运行时内存)
* CLASS : 编译后
* RUNTIME : 运行时
* 比如: IO流(过河拆桥,用完后即时释放), 泛型(泛型只在编译阶段,运行时也被擦除)
*/
@MyAnno
public class Demo02 {
@MyAnno
public static void main(@MyAnno String[] args) {
@MyAnno
int i;
}
}
@interface MyAnno{
String name() default "yy";
int[] array();
}
Player表示玩家,Data表示注册账号日期。
package com.itheima04.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 元注解: 用来描述注解的注解 (初始的注解)。 元数据 : 诠释数据的数据(如右击一个图片属性查看)
* name = 张裕
* 属性(元数据) = 值(数据)
*
* 两个元注解:
* 1. @Target : 适用范围
* 1. ElementType.Type : 四大java类型上可用
* 2. ElementType.Method : 方法上可用
* 3. ElementType.Constructor : 构造上可用
* 4. ElementType.Field:属性上可用
*
* 2. @Retention : 保留(生命周期)。源码,编译,运行
* 1. SOURCE :编译后target文件夹下YourAnno.class中没有@YourAnno,override注解就是保留在SOURCE源码
* 2. CLASS :编译后target文件夹下YourAnno.class中有@YourAnno,和上面源码一样
* 3. RUNTIME : 常用,加载到jvm里注解还在,可用反射clazz.getDeclaredAnnocation和
* clazz.isAnnotationPresent(YourAnno.class)查看有没有。
*
* 注解的使用: 1. 可以放在代码上
* 2. 使用注解的时候,如果有属性没赋值,必须赋值
* 3. 特殊: 如果注解中的属性有且仅有一个未赋值,并且属性名为value,在赋值的时候,value= 可省略
*/
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//@YourAnno(name="yy") //注解的使用
//@YourAnno(value="yy") //把String value();放开未赋值,对应上面注释中注解使用中的2
//@YourAnno("yy") //把String value();放开未赋值,对应上面注释中注解使用中的3
public class Demo03 {
@YourAnno
public static void main(String[] args) {
}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//@Target(value={ElementType.METHOD,ElementType.TYPE}) //枚举名.属性 //如下图的提示
//@Target({ElementType.METHOD,ElementType.TYPE}) //缩略写法,override只适用于方法,类型不能用,如下改进
@Target(ElementType.METHOD)//在注解中,如果数组只有一个元素,{}是可以省略的
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Retention(RetentionPolicy.SOURCE)
@interface YourAnno{
String name() default "xx";
// String value(); //属性,类似方法
}
如下函数里调用了MyAnno注解,形成联系。
推测springboot项目如何利用注解运行起来的呢?
比如spring的bean怎么初始化的呢?
为什么能在一个类上写一个@component,@Service, @controller这些注解就能将这个类作为单例spring bean注入到spring工厂中去呢?
如下.run(传入本类)。
@SpringBootApplication默认扫描本包下所有类如下。
如下也new Svc1放入spring工厂,这就是注解和反射配合使用的方式。注解本身没有太大作用,注解源码就几行。发挥作用的是扫描注解的反射部分,针对注解的类型和注解里的字段进行相应的配置。
9.自定义junit测试框架并打jar包:method.isAnnotationPresent
package com.itheima05.custom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* 思路: @MyTest替换@Test
* 1. 自定义MyTest注解
* 1. 属性 : 无 (有属性必须要赋值,下面没用到MyTest里没有赋值)
* 2. 元注解: 1. 指定作用域: Method
* 2. 生命周期: runtime(注解保留到运行时期才能碰到反射)
*
* 2. 反射 : 在运行时操作类
* 需求: 在方法上 加了 MyTest注解的方法能够像main方法一样运行,所以用到反射
* 1. 获取TestDemo 的Class对象
* 2. 获取类中的所有public方法
* 3. 判断每个方法上是否有注解MyTest,如果有,就运行
*/
public class CustomDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> clazz = Class.forName("com.itheima05.custom.TestDemo");
Object obj = clazz.newInstance();
Method[] methods = clazz.getMethods(); //注意是getMethods最后有一个s,返回数组
for (Method method : methods) {
boolean result = method.isAnnotationPresent(MyTest.class); // 方法上是否有指定注解,如果有返回true,这行和MyTest形成联系
if(result){
// method.invoke(obj); //普通方法调用,第一个参数是调用此方法的对象 //这行不用线程
// 如下有异常时程序要不影响其他方法输出,所以每个方法用一个线程
new Thread(() -> {
try {
method.invoke(obj);
}catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
}
package com.itheima05.custom;
public class TestDemo {
public void method00(){
System.out.println("方法0");
}
@MyTest
public void method01(){
System.out.println("测试方法一");
}
@MyTest
public void method02(){
int i = 1/0; //异常不能影响其他,所以多线程
System.out.println("测试方法二");
}
@MyTest
public void method03(){
System.out.println("测试方法三");
}
}
CustomDemo和MyTest两个类即CustomDemo.java文件
可以打成jar包:New Project-Maven。
如下选中java文件夹右击New-Package,命名为com.nb.xunit包,将CustomDemo和MyTest两个文件复制到该包下。MyTest注解应该给public,不然只能在本包下使用。
如下操作后可以删除CustomDemo.java和MyTest.java。
10.类加载器:.class.getClassLoader(),引扩应,双亲委派
package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
* 0. 类 .java文件 -> .class文件 -> Class对象(内存中)
* 源码 编译后 运行时
* 当.class文件被加载进内存,JVM会在堆中创建一个Class对象
*
* 1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
*
* 2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
* 1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
* 2. 扩展类加载器 extension。ext包下
* 3. 应用/系统 类加载器 application。 第三方编写的类
*
* Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
*
* 补充: 1. 三种类加载器存在继承关系的【不叫继承 (叫组合 composition,因为引导类加载器用C写的)】
* 2. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
* 类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
*
* 什么情况下, 一个.class文件的Class对象会有多个?
* 多个类加载器(程序员自定义类加载器,手动操作类加载器去加载)去加载同一个.class文件
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Class clazz = DNSNameService.class; // Ext扩展包下
ClassLoader loader2 = clazz.getClassLoader();
System.out.println(loader2); //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ClassLoader loader3 = String.class.getClassLoader();
System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
}
@Test
public void method01(){ //三种类加载器的关系:继承关系,组合关系,因为跨语言了
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用
ClassLoader loader2 = loader1.getParent(); //获取父加载器
System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);//null //引导 (父父)
}
}
11.代理租房:静态代理,中介可代理实现FD接口的多个房东。装饰者设计模式(IO流,可层层增加功能),迭代器设计模式(集合)
代理更多的是强调对对象的访问控制
,比如访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆。
就是将某个控制访问权限应用到多个对象
上,使用的是代理对象(中介)
在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。
而装饰器更多的强调给对象加强功能
,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象
上。装饰是使用的调用者从外部传入的被装饰对象,调用者只想要你把他给你的对象
装饰(加强)一下。
package com.itheima02.proxy;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
/*
* 代理: 一个代理对象(中介) 拥有 被代理对象(房东) 的大部分权限, 控制外界(租客)对被代理对象的访问
* 1. 被代理类对象(房东)
* 2. 代理类对象(中介) 。代理对象 控制 外界对被代理类对象的 '直接'访问,找中介间接访问房东
*
* 模型: 1. 中介 拥有 房东 的 大部分权限:代理类对象 拥有 被代理类对象的大部分方法
* 解决: 不用继承,用接口
* 2. 中介要控制外界对房东的访问:中介要拥有房东的联系方式, 要有随时找得到房东
* 解决: 将房东作为中介的属性存在
*
* 问题1: 我们没有限制对房东的直接访问, 用代理模式意义何在?
* 意义在于, 我们可以在不修改房东类的情况下, 限制访问条件
* 1. 假设 有个类 属于 某个jar包, 这个类改不了的
* 2. 我们又想改这个类的某些方法的访问条件 -> 代理模式
*
* 问题2: 继承同样也是实现上述需求, 为什么要用代理模式? 代理模式的扩展性更强
* 继承: 只扩展了对FangDong类的访问限制
* 代理: 扩展了对Fd接口 所有实现类对象的 访问限制(如还代理其他房东)
*
* BufferedReader : 代理模式(又称装饰设计模式)
* new BufferedReader(new FileReader(a.txt)); //里面new FileReader(a.txt)是房东
* BufferedReader是中介,也可以代理除了 FileReader还有Reader其他实现类对象 (多态)
*/
public class ProxyDemo {
public static void main(String[] args) throws FileNotFoundException {
FangDong fangDong = new FangDong(); //使用的时候: 先创建被代理类对象
ZhongJie zh = new ZhongJie(fangDong); //接着: 创建代理类对象
zh.zufang(999); //钱不够, 房子不租给你 //访问中介,实际上通过中介访问房东
}
}
package com.itheima02.proxy;
class FdSon extends FangDong{ //继承:也没改FangDong类,但只扩展了对FangDong类的访问限制,只能单继承
@Override
public void zufang(int money) {
if(money > 1000){
super.zufang(money);
}
}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111
public class FangDong implements Fd{
public void zufang(int money){
System.out.println("房东出租房子: " + money);
}
public void maifang(int money){
System.out.println("房东卖掉房子:" + money);
}
public void buyfang(int money){ //这权限不给中介
System.out.println("房东买入房子作为投资 :" + money);
}
}
interface Fd{ //房东类中的方法抽取
void zufang(int money);
void maifang(int money);
}
class ZhongJie implements Fd{ //fd接口也可以其他类实现,所以ZhongJie类构造里传入其他类相当于多继承
Fd fd; //房东对象 定义并没有实例化像string num
public ZhongJie(Fd fd){ //fd实例化,当我们创建中介对象的时候,必须要指定他所代理的房东对象
this.fd = fd;
}
@Override
public void zufang(int money) {
if(money > 1000){ //租客找中介租房, 当钱>1000, 中介就会帮租客联系房东签合同
fd.zufang(money);
}else{
System.out.println(" 钱不够, 房子不租给你");
}
}
@Override
public void maifang(int money) {
}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
//下面为了解释proxy.zufang(1001)调用h.invoke 。下行Fd接口已通过Proxy.newProxyInstance方法中第二个参数传入了。
class MyProxy implements Fd{ //JVM底层动态创建中介类,Proxy.newProxyInstance方法结果
InvocationHandler h;
public MyProxy(InvocationHandler h){ //h是实现类对象,不是接口
this.h = h;
}
@Override
public void zufang(int money) { //动态变化内容, JVM不能写, 交给程序员如下
// Method method = this.getClass().getMethod("zufang",int.class); //方法的反射,区分zufang还是maifang
// Object[] param = {money};
// h.invoke(this,method,param);
}
@Override
public void maifang(int money) {
// h.invoke();
}
}
12.动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
* 动态代理(比上面多一个反射而已): 动态: 代理中介类不是静态定义,是动态生成的
* 1. 静态定义: 把这个类写死了即上面class ZhongJie
* 2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口
*/
public class DynamicDemo { //Dynamic反义词static
public static void main(String[] args) {
FangDong fd = new FangDong(); //先看上房东房子
/*
static Object newProxyInstance(ClassLoader loader, //这个方法最终目的: 创建一个中介类对象!
Class<?>[] interfaces,
InvocationHandler h)
1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
一般是应用类加载器(第三方类)。 直接 被代理类(房东类) 的类加载器
2. interfaces : 接口的Class对象(让中介类实现的)
就是 被代理类(房东类) 所实现的接口
class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),上面1。
这个中介类需要实现接口从而拥有被代理类方法,上面2。
接下来需要重写接口里方法,下面3。
3.InvocationHandler h : 调用处理器 (InvocationHandler是一个接口)
invoke方法可以看成中介类对接口所有方法的重写
*/
ClassLoader loader = fd.getClass().getClassLoader(); //对应上面1
Class<?>[] interfaces = fd.getClass().getInterfaces();//获取此类实现的所有接口,对应上面2
// Class<?>[] interfaces = {Fd.class}; //效果同上行
// Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行
// 实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。
// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.com.itheima03.dynamic.Fd] //就是FD接口
InvocationHandler h = new InvocationHandler() { //h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
/*
* invoke函数参数: 1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
* 2. method: 代理类对象当前调用的方法
* 3. args: 代理类对象当前调用方法传入的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
System.out.println(Arrays.toString(args));
return null;
}
};
//11111111111111111111111111111111111111111111如上是Proxy.newProxyInstance方法的三个参数获取
Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy
//如上行平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以上行用Fd(这个Fd是FangDong类下面定义的)
proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
//proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
}
}
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
* 动态代理 比之 普通代理: 好处在于 不用事先定义类, 代理类在运行时动态生成 (反射)
* 运用场景: 被代理对象(房东) 有 10000万个方法, 你只想修改其中一个
*/
public class Demo02 {
public static void main(String[] args) {
FangDong fd = new FangDong(); //先看上房东房子
ClassLoader loader = fd.getClass().getClassLoader();
Class<?>[] interfaces = fd.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if("zufang".equals(name)){
int money = (int) args[0]; // Object[] param = {money};
if(money > 1000){
fd.zufang(money);
}else{
System.out.println("钱不够...");
}
}/*else if("maifang".equals(name)){
}*/else{
method.invoke(fd,args); //中介类.其他9000个方法,走这行,即其他9000个方法依然交给房东fd自己处理
}
return null;
}
};
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
proxy.zufang(888);
// proxy.maifang(3000);
}
}
13.模板:类里有抽象方法必须抽象类
package com.atguigu.test02.abstract_;
// 编写一个类,包含一个方法,可以统计 你执行任意代码的运行时间。
public class TestTemplate {
public static void main(String[] args) {
MyCalTime my = new MyCalTime();
long time = my.getTime();
System.out.println("耗时:" + time + "毫秒");
}}
//111111111111111111111111111111111111111111111111111111111111111111111
abstract class CalTime{
public final long getTime(){//可以计算任意一段代码的运行时间 //这里加final的目的是不希望子类重写,改写我的算法的结构
long start = System.currentTimeMillis(); //(1)获取开始时系统时间
doWork(); //(2)执行xxxx
long end = System.currentTimeMillis(); //(3)获取结束时系统时间
return end - start; //(4)计算时间差
}
protected abstract void doWork(); //protected的目的,希望只是子类中进行访问和重写
}
//111111111111111111111111111111111111111111111111111111111111111111
class MyCalTime extends CalTime{
@Override
protected void doWork() { //重写抽象方法
long sum = 0;
for(int i=1; i<=100000; i++){
sum += i;
}
System.out.println("sum = " + sum);
}
}
14.单例:某个类只能有唯一的一个实例对象,多线程
package com.atguigu.test17;import org.junit.Test;
public class Test17 {
@Test
public void test1(){
SingleEnum s1 = SingleEnum.INSTANCE;
SingleEnum s2 = SingleEnum.INSTANCE;
System.out.println(s1 == s2); //true
}
@Test
public void test2(){
// SingleEnum.test();
//此时我并没有需要用到这个INSTANCE对象,但是它也创建出来SingleEnum对象,单例恶汉式
}
@Test
public void test3(){
SingleClass s1 = SingleClass.INSTANCE;
SingleClass s2 = SingleClass.INSTANCE;
System.out.println(s1==s2); //true,地址一样,只有一个对象。
}
@Test
public void test4(){
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2); //true
}
@Test
public void test5(){
LazyClass s1 = LazyClass.getInstance(); //getInstance()里必有new
LazyClass s2 = LazyClass.getInstance();
System.out.println(s2 == s1);
}
LazyClass s1;
LazyClass s2;
@Test
public void test6(){
//匿名的内部类,继承Thread类。=后面是子类,然后多态
Thread t1 = new Thread(){
public void run(){
s1 = LazyClass.getInstance();
}
};
Thread t2 = new Thread(){
public void run(){
s2 = LazyClass.getInstance();
}
};
t1.start();
t2.start();
try {
//这里用join的目的是,为了两个子线程都执行完,再执行主线程的System.out.println(s1);
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} }
//11111111111111111111饿汉式: 不管我们使用者是否需要这个对象,它都上来先给你创建好这个唯一的对象。
//形式一:对应test1()
enum SingleEnum{
INSTANCE; //单例,枚举只有一个
//public static void test(){ //用这个方法不用上面这个INSTANCE对象,对应test2() }
}
//形式二:对应test3()
class SingleClass{ //1.5之前老版的枚举 //用一个全局的静态的常量,来保存这个唯一的实例对象
public static final SingleClass INSTANCE = new SingleClass();
private SingleClass(){
}}
//形式三:对应test4()
class Single{
//用一个私有的静态的常量,来保存这个唯一的实例对象
private static final Single INSTANCE = new Single();
private Single(){
}
public static Single getInstance(){ //提供一个静态方法,来返回这个常量对象
return INSTANCE;
}}
//111111111111111111111懒汉式: 延迟创建对象。当使用者来或者这个对象,要用到对象时,我再创建。
//形式一: 对应test6()
class LazyClass{
private static LazyClass instance; //不加final,可以不用new出来 //加final必须new SingleClass()
private LazyClass(){
}
public static LazyClass getInstance(){
if(instance == null){//提高效率,已创建对象就没必要再进去,直接最后return instance; 未创建对象进入
synchronized(LazyClass.class){ //当前类的Class对象,静态方法不能出现this
if(instance == null){//安全判断
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazyClass();
}
}
}
return instance;
}
//形式二:内部类
class Lazy{
private Lazy(){}
private static class Inner{
public static final Lazy INSTANCE = new Lazy();//在内部类中,创建外部类的唯一对象
}
public static Lazy getInstance(){
return Inner.INSTANCE; //用到getInstance方法才会new Lazy(),也是懒汉式
}
}
15.工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()
package com.atguigu.test12;import org.junit.Test;
//1、简单工厂模式:优点:代码比较简洁。缺点:如果增加新的产品类型(奥迪),需要修改工厂类中getCar()。
//违反了面向对象的一个开发原则:对扩展开放,对修改关闭。public class TestFactory {
@Test
public void test03(){
Car c = SimpleFactory2.getCar("奔驰");
c.run();
}
@Test
public void test02(){
Car c = SimpleFactory.getCar(); //静态,类名. 不用创建工厂类对象
c.run();//这里也是运行宝马的run(),但是从头至尾没有出现BMW类,解耦合
}
@Test
public void test01(){ //没有工厂,如下创建对象和使用对象是同一个人完成
BMW b = new BMW(); //比如spring容器,new时要初始化很多配置信息才创建一个对象
b.run(); //使用对象,调用方法
}}
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Car{ //对应test01()
void run();
}
class BMW implements Car{
@Override
public void run() {
System.out.println("宝马让你在车里面哭");
}
}
class Benz implements Car{
@Override
public void run() {
System.out.println("奔驰让你在车盖上哭");
}
}
class Audi implements Car{ //多了一类车,下面工厂类需要再改变,这就是简单工厂模式
@Override
public void run() {
System.out.println("奥迪让你在...");
}
}
//1111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory{ //对应test02()
public static Car getCar(){
return new BMW();
}
}
class SimpleFactory2{ //对应test03()
public static Car getCar(String type){
if("宝马".equals(type)){
return new BMW();
}else if("奔驰".equals(type)){
return new Benz();
}
return null;
}
}
package com.atguigu.test12;
/*
* 2、工厂方法模式:(1)为了生产对象与使用对象分开 (2)如果增加新产品,就不需要修改原来的工厂类
* 优点:遵循了增加新产品,不修改原来的类的原则。 缺点:类太多了,一种车一个工厂
*/
public class TestFactory2 {
public static void main(String[] args) {
BaoMaFactory bf = new BaoMaFactory();
Che c = bf.getChe();
c.run();
}
}
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Che{
void run();
}
class BaoMa implements Che{
@Override
public void run() {
System.out.println("宝马");
}
}
class BenChi implements Che{
@Override
public void run() {
System.out.println("奔驰");
}
}
class AoDi implements Che{
@Override
public void run() {
System.out.println("奥迪");
}
}
//1111111111111111111111111111111111111111111111111111111111111如下工厂接口
interface GongChang{
Che getChe();
}
class BaoMaFactory implements GongChang{
@Override
public Che getChe() {
return new BaoMa();
}
}
class BenChiFactory implements GongChang{
@Override //只需要增加一个工厂类,专门生产奥迪车,不动上面代码
public Che getChe() {
return new BenChi();
}
}
如下用反射结合上面的简单工厂和工厂方法:
package com.atguigu.test12;
public class TestFactory3 {
public static void main(String[] args)throws Exception {
Vehicle c = SimpleFactory3.getVehicle("com.atguigu.test12.QQ");
c.run(); //qq车
Vehicle c2 = SimpleFactory3.getVehicle("com.atguigu.test12.Aoto");
c2.run(); //奥拓
}
}
//11111111111111111111111111111111111111111111111111111111111111111中间没变
interface Vehicle{
void run();
}
class QQ implements Vehicle{
@Override
public void run() {
System.out.println("qq车");
}
}
class Aoto implements Vehicle{
@Override
public void run() {
System.out.println("奥拓");
}
}
//1111111111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory3{
public static Vehicle getVehicle(String className)throws Exception{
Class clazz = Class.forName(className);
return (Vehicle) clazz.newInstance(); //clazz.newInstance()返回Object强转为Vehicle类
}
}
16.策略+工厂:去除if-else
16.1 未引入设计模式
1.超级会员
折扣8折、普通会员
折扣9折、普通用户没有折扣;
2.专属会员
要在店铺下单金额大于30元时享受7折优惠;
3.用户超级会员到期,并到期时间在一周内,对用户的单笔订单按照超级会员进行折扣,且只折扣一次。
public BigDecimal calPrice(BigDecimal orderPrice, String buyerType){
if(用户是专属会员){
if(订单金额大于30元){
return 7折价格;
}
}
if(用户是超级会员){
return 8折价格;
}
if(用户是普通会员){
if(该用户超级会员刚过期且尚未使用过临时折扣){
临时折扣使用次数更新();
return 8折价格;
}
return 9折价格;
}
return 原价;
}
16.2 引入策略模式
策略/接口UserPayService
。
public interface UserPayService{
public BigDicimal quote(BigDecimal orderPrice); //计算应付价格
}
策略类ParticularlyVipPayService,SuperVipPayService ,VipPayService
。
public class ParticularlyVipPayService implements UserPayService{ //专属会员
@Override
public BigDicimal quote(BigDecimal orderPrice){
if(消费金额大于30元){
return 7折价格;
}
}
}
public class SuperVipPayService implements UserPayService{ //超级会员
@Override
public BigDicimal quote(BigDecimal orderPrice){
return 8折价格;
}
}
public class VipPayService implements UserPayService{ //普通会员
@Override
public BigDicimal quote(BigDecimal orderPrice){
if(该用户超级会员刚过期且尚未使用过临时折扣){
临时折扣使用次数更新();
return 8折价格;
}
return 9折价格;
}
}
价格计算方法calPrice
,如下可看出引入策略模式代码维护性和可读性有所增强,但是并未消除if-else。
public BigDecimal calPrice(BigDecimal orderPrice, User user){
String vipType = user.getVipType();
if(vipType == 专属会员){
UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);
return strategy.quote(); //伪代码
}
if (vipType == 超级会员) {
UserPayService strategy = Spring.getBean(SuperVipPayService.class);
return strategy.quote(orderPrice);
}
if (vipType == 普通会员) {
UserPayService strategy = Spring.getBean(VipPayService.class);
return strategy.quote(orderPrice);
}
return 原价;
}
16.3 引入工厂模式
工厂类UserPayServiceStrategyFactory
。
public class UserPayServiceStrategyFactory{
private static Map<String, UserPayService> services = new ConcurrentHashMap<String, UserPayService>();
public static UserPayService getByUserType(String type){
return services.get(type);
}
public static void register(String userType, UserPayService userPayService){
Assert.notNull(userType, "userType can't be null");
services.put(userType, userPayService);
}
}
计算价格方法calPrice
,如下可看出引入工厂模式后,完全去除了代码中的if-else。
public BigDecimal calPrice(BigDecimal orderPrice, User user){
String vipType = user.getVipType();
UserPayService strategy = UserPayServiceStrategyFactory.getByUser(vipType);
return strategy.quote(orderPrice);
}
在Spring初始化时,需要将策略模式类都注册到工厂类中,将策略类进行改造,使用Spring的InitializingBean接口,在bean初始化后,执行afterPropertiesSet方法进行注册。
//专属会员
public class ParticularlyVipPayService implements UserPayService, InitializingBean{
@Override
public BigDicimal quote(BigDecimal orderPrice){
if(消费金额大于30元){
return 7折价格;
}
}
@Override
public void afterPropertiesSet(){
UserPayServiceStrategyFactory.register("ParticularlyVip", this);
}
}
//超级会员
public class SuperVipPayService implements UserPayService, InitializingBean{
@Override
public BigDicimal quote(BigDecimal orderPrice){
return 8折价格;
}
@Override
public void afterPropertiesSet(){
UserPayServiceStrategyFactory.register("SuperVip", this);
}
}
//普通会员
public class VipPayService implements UserPayService, InitializingBean{
@Override
public BigDicimal quote(BigDecimal orderPrice){
if(该用户超级会员刚过期且尚未使用过临时折扣){
临时折扣使用次数更新();
return 8折价格;
}
return 9折价格;
}
@Override
public void afterPropertiesSet(){
UserPayServiceStrategyFactory.register("Vip", this);
}
}
17.DTD约束:配置文件
用word/txt(和xml一样都没有格式约束
)传输数据,但xml格式规范
。
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 如上行文档声明 : 版本 和 编码 -->
<!--
xml基本语法:
1. 标签分类 :
1. 成对标签 : start tag - 内容 - end tag
开标签 闭标签
<标签名> </标签名>
2. 单独标签 : 自关闭标签 <标签名/>
2. xml有且仅有一个根标签 root tag (第一个标签,xml中的所有内容都在内)
3. 标签可以拥有属性的(开标签和自关闭标签) key=value 即 id="003"
了解:
实体字符(转义字符):
< -> < less than即缩写lt
> -> > greater than
< > 小于或大于就是不等于,前端支持,java不支持
CDATA: unparsed character data 不解析的字符数据
在这里的内容是不会被解析器所解析(看成注释)
-->
<list>
<![CDATA[" if(x < > 1){ sout: xx} "]]>
<hehe id="003"/>
<book id="001">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
<extra>if(x < > 1){ sout: xx}</extra>
</book>
<book id="002">
<name>java从入门到放弃</name>
<desc>人生的最后一本</desc>
<price>11</price>
</book>
</list>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
可扩展: 开发者随意命名标签
存数据 为了 以后 取数据(解析 parse)
问题:会给数据解析带来麻烦
解决: 约束(规则,规范)给xml指定约束, 其内部拥有什么内容,以及内容的编写格式都有要求
规则: list里面必须是book标签(多个)
book标签里还可以有name desc price等标签
这些标签,里面有文本内容
两种约束: DTD: document type definition 文档类型定义 (了解)
Schema : 模式 (了解) 替代DTD,全称(XML Schema Definition,XSD)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!--
DTD约束: 1. 根标签 必须是list
2. list里有book (+ : 表示1个或多个book标签)
3. book里有name desc price
4. name desc price = PCDATA(parsed character data) 可以包含文本
5. book 有一个属性 id 默认值 为 999
如下写完约束后,会自动提示
-->
<!DOCTYPE list [
<!ELEMENT list (book+)>
<!ELEMENT book (name,desc,price)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT desc (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ATTLIST book id CDATA "999">
]>
<!--111111111111111111111111111111111111111111111111111111111111111-->
<list>
<book id="001">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
</book>
<book id="002">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
</book>
</list>
18.schema约束:命名空间
//05_book.xsd
<?xml version='1.0' encoding='UTF-8' ?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='http://www.itheima.com'
elementFormDefault="unqualified">
<!--
上面几行就是命名空间技术,需要被另一个文件引用即06_book.xml引用才可以生效。
schema约束: 约束规则用xml语法来书写的。
element : 元素(开标签到闭标签之间的内容,我们统称为标签)
complexType : 复杂类型(但凡元素有子标签或属性)
sequence : 序列 (子元素的序列)
maxOccurs: 最大出现次数,如下书标签最少出现一次,至多随意
-->
<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->
<xs:element name='书架' >
<xs:complexType>
<xs:sequence maxOccurs='unbounded' minOccurs="1">
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:string' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:string' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
//06_book.xml调用05_book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<itheima:书架 xmlns:itheima="http://www.itheima.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itheima.com 05_book.xsd">
<!--11111111111111111111111111111111111111111111111111111111111111111111111-->
<书>
<书名>呵呵</书名>
<作者>啦啦</作者>
<售价>嘻嘻</售价>
</书>
</itheima:书架>
如下xml用到在线的xsd,自动补全。
19.xml解析:dom4j
dom4J不是sun公司开发的,所以放入lib目录,add as Library相当于解压
,sun公司开发的放External libraries。SAX解析读完一行解析完就可以当时释放解析内容,不耗内存但慢。SAX的变形pull。
package com.itheima04.parse;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
public class ParseDemo {
public static void main(String[] args) throws DocumentException {
SAXReader reader = new SAXReader();
//将下面这个文件读到内存中
Document document = reader.read("02_book.xml"); //document文档对象 表示 02_book.xml文件的
Element rootElement = document.getRootElement(); //解析过程: 爬树
List<Element> books = rootElement.elements("book"); //获取根元素 store的所有book子元素
for (Element book : books) {
String name = book.elementText("name"); //获取子标签内部的文本数据
String desc = book.elementText("desc");
String price = book.elementText("price");
String id = book.attributeValue("id"); //获取book标签的属性id的值
System.out.println(id + "," + name + "," + desc + "," + price);
}
}
}
20.javase总结:IO流,集合
1.
ArrayList
底层是数组,默认长度为10,扩容系数1.5即下次15,ArrayList查询复杂度为o(1)。LinkedList
底层双向链表,好处不用进行扩容,一直往后追加,坏处是要找第n个元素时间复杂度是o(n)。
2.
HashMap
底层也是数组,数组大小必须是2的幂次方倍【确保数组中的每一个位置发生hash冲突的概率相同】。LinkedHashMap
底层和HashMap一样,只不过每个元素有一个指针指向下一个插入的元素,有序了。TreeMap
底层红黑树实现,特点是按照key进行排序。HashTable
线程安全(读写整个数组结构加了syn修饰即加了锁)(HashMap线程不安全,两者底层存储结构一样)。锁住整个结构代价大,所以出现了concurrent HashMap
,只锁住数组的一个桶。HashSet将HashMap的value设为null。
源码穿透:JDK -> JVM -> glibc(系统调用的封装)->Linux内核
。进程是资源分配的最小单位。
21.求一个长度为10的整型数组的最大元素,采用随机的方式赋值并输出各元素的值?