第三周笔记
day01
字符流与字节流
- 字符流:----传输的是字符,只能传输字符
-
write() write(字符数组) write(字符串)字符写出流 Writer
字符读入流 Reader read() read(数组)
字符缓冲写出流 BufferedWrite newline() 跨平台换行
字符缓冲读入流 BufferedReader readLine() 读一行
-
- 字节流:—传输的是字节,可以传输任何类型的数据
-
字节输出流 OutputStream 字节输入流 InputStream 字节缓冲输出流 BufferedOutputStream 字节缓冲输入流 BufferedInputStream
装饰设计模式:
装饰设计模式:基于已经实现的功能,提供增强的功能.
装饰设计模式的由来就来自于对缓冲流的实现.
特点: 从缓冲流的角度讲解
- 1.使流原来的继承体更加的简单
- 2.提高了效率
- 3.由于是在原有的基础上提高增强的功能,所以他还要属于原来的体系
步骤:
- 1.让BTest 继承自Test
- 2.在BTest内有一个Test类型的成员变量
- 3.通过BTest内一个带参数的构造方法接收外部传入的一个Test类型的对象,交给内部的Test的属性
- 4.在实现功能的时候,调用传入的Test类型的对象实现原有的功能,自己实现增强的功能.
适配器设计模式:
通常可以变相的理解成装饰设计模式
模拟字符缓冲读入流:
分析:
- 1.要属于流的体系
- 2.要有一个Reader类型的成员变量
- 3.要有一个带参数的构造方法接收外部传入的流对象
- 4.模拟readLine(),实现读一行的功能
- 5.关闭流
LineNumberReader:
LineNumberReader:是BufferedReader的子类,不能读.但是可以提高效率,特有功能:设置行号,获取行号
设置行号,默认从0开始,从1开始打印
lineNumberReader.setLineNumber(10);//从11行开始打印
LineNumberReader lineNumberReader = new LineNumberReader(new FileReader("D:\\workspace/BigData1923N19\\src\\com\\qf\\test\\Demo1.java"));
//设置行号,默认从0开始,从1开始打印
lineNumberReader.setLineNumber(10);
String data = null;
while ((data = lineNumberReader.readLine()) != null) {
System.out.print(lineNumberReader.getLineNumber());//获取行号
System.out.print(data);
System.out.println();
}
lineNumberReader.close();
字节流读取
public static void main(String[] args) throws IOException {
//写入数据
write();
//读1
read1();
//读2
read2();
//读3
read3();
}
//将数据写入磁盘
public static void write() throws IOException {
//1.创建字节输出流并关联文件
FileOutputStream fileOutputStream = new FileOutputStream("test1.txt");
//2.写--这里只能写字节
fileOutputStream.write("bingbing".getBytes());
//3.关闭资源
fileOutputStream.close();
}
//读1 一次读一个字节
public static void read1() throws IOException {
//1.创建字节输入流并关联文件
FileInputStream fileInputStream = new FileInputStream("test1.txt");
//2.读
int num = 0;
while ((num = fileInputStream.read()) != -1) {
System.out.print((char)num);
}
//3.关闭资源
fileInputStream.close();
}
//读2 一次读多个字节
public static void read2() throws IOException {
//1.创建字节输入流并关联文件
FileInputStream fileInputStream = new FileInputStream("test1.txt");
//2.读
int num = 0;
byte[] bytes = new byte[5];
while ((num = fileInputStream.read(bytes)) != -1) {
System.out.print(new String(bytes,0,num));
}
//3.关闭资源
fileInputStream.close();
}
//读3 一次全部读完
public static void read3() throws IOException {
//1.创建字节输入流并关联文件
FileInputStream fileInputStream = new FileInputStream("test1.txt");
//获取文件的字节个数
//注意:这种方式适合文件的字节数比较小的时候,大概是几kb之内.
int num1 = fileInputStream.available();
//2.读
byte[] bytes = new byte[num1];
fileInputStream.read(bytes);
System.out.println(new String(bytes));
//3.关闭资源
fileInputStream.close();
}
}
标准输入流和标准输出流(默认字节流)
-
标准输入流:–System.in:“标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
输入源:可以发送数据的设备 输出源:可以接收数据的设备
1.当前的流已经打开并关联了输入源–键盘
2.如果不想让键盘充当输入源,可以通过setIn进行更换
3.是一个字节流 -
标准输出流:–System.out
阻塞式方法
InputStream inputStream = System.in;
//int num = inputStream.read();//把标准输入流的read方法称为阻塞式方法
转换流
- 转换流:实现的是从字节流到字符流,本身是字符流
- 模拟的场景:使用字符缓冲流的readline和newline方法实现字节流的功能
- InputStreamReader:输入转换流
- OutputStreamWriter:输出转换流
以reader 和writer结尾的是字符流
以stream结尾的是字节流
public static void main(String[] args) throws IOException {
//1将标准字节输入流转成字符缓冲读入流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
//2.将标准字节输出流转成字符缓冲写出流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
//3.读写
String data = null;
while ((data = bufferedReader.readLine()) != null) {
bufferedWriter.write(data);
bufferedWriter.newLine();
bufferedWriter.flush();
//当输入的内容是over时,结束
if (data.equals("over")) {
break;
}
}
bufferedReader.close();
bufferedWriter.close();
}
更换数据源
//注意:这里是临时更换,只能在当前程序中使用更换后的输入源输出源,如果在其他的程序中会自动变回原来的输入源输出源
//从键盘接收数据更替成从文件接收数据
System.setIn(new FileInputStream(“D:\workspace\BigData1923N20\src\com\qf\test\Demo1.java”));
//从输出到控制台更替成输出到文件
System.setOut(new PrintStream(“D:\workspace\BigData1923N20\Democopy.java”));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
打印流
字节打印流:PrintStream:除了拥有输出流的特点之外,还有打印的功能.
字符打印流:PrintWriter
字符比字节多一种字符流
字节打印流支持的设备:
- 1.File类型的文件
- 2.字符串类型的文件
- 3.字节输出流
Properties
properties: 实际上就是Map集合,存储的是属性,属性以键值对的方式存储.这里的键和值都必须是字符串.所以不需要考虑泛型
为什么要在这里讲Properties?
因为他的使用与流紧密相关
优点:
- 1.以键值对的形式存储数据
- 2.内部针对属性的存储封装了大量的专有方法:load(读入),store(写出),list
1.Properties的基础
public static void fun1() {
//创建对象
Properties properties = new Properties();
properties.setProperty("a", "java");
properties.setProperty("b", "html");
System.out.println(properties);
//根据key取值
System.out.println(properties.getProperty("a"));
//key是唯一的
Object object = properties.setProperty("a", "BigData");
System.out.println(object);
//当前的key在Properties中不存在时,会打印python
System.out.println(properties.getProperty("c", "python"));
}
2.利用Properties获取系统属性
public static void fun2() {
//获取系统属性
Properties properties = System.getProperties();
//遍历
Set<String> set = properties.stringPropertyNames();
Iterator<String> iterator =set.iterator();
while(iterator.hasNext()){
String string = (String)iterator.next();
System.out.println("key:"+string+" value "+properties.getProperty(string));
}
//增强for循环
for(String key : set){
System.out.println("key:"+key+" value "+properties.getProperty(key));
}
//对系统的某个属性进行修改
properties.setProperty("sun.jnu.encoding", "UTF8");
System.out.println(properties.getProperty("sun.jnu.encoding"));
//重新获取一遍值
//原理:会先到内存中找属性集合的对象,如果有,直接使用.如果没有,会重新初始化一个新的对象,并获取属性集合.
Properties properties1 = System.getProperties();
System.out.println(properties1.getProperty("sun.jnu.encoding"));
properties.list(System.out);
}
3.Properties在实际中的使用
public static void fun3() throws FileNotFoundException, IOException{
//创建properties对象
Properties properties = new Properties();
//利用load方法将内容从磁盘读到内存
//注意:使用的文件内容的格式应用时key=value
properties.load(new FileReader("test01.txt"));
properties.list(System.out);
//更改内容
properties.setProperty("a", "woaini");
//写入磁盘,第二个参数是提醒信息
properties.store(new FileWriter("test01.txt"), "hello wuxi");
}
序列化(demo13)
-
序列化流: 将短期存储的数据实现长期存储,这个过程对应的流就是序列化流
-
序列化: 将数据从内存放入磁盘,可以实现数据的长久保存–数据持久化的手段
-
反序列化: 将数据从磁盘放回内存
-
数据的存储分为两类 短期存储:存放在内存中,随着程序的关闭而释放—对象,集合,变量,数组 长期存储:存储在磁盘中,即使程序关闭了,数据仍然存在------文件
-
进行序列化的步骤:----通过对象的序列化讲解 1.创建一个类 2.使用对应的流将对象存入磁盘中----序列化----ObjectOutputStream 3.使用对应的流将对象从磁盘中取出放回内存–反序列化------ObjectInputStream 4.关闭流
-
注意点:序列化流在工作时也要关联对应的基本的输入流和输出流
-
进行序列化的步骤:–通过对象的序列化讲解
-
1.创建一个类
-
2.使用对应的流将对象存入磁盘中----序列化----ObjectOutputStream
-
3.使用对应的流将对象从磁盘中取出放回内存–反序列化------ObjectInputStream
-
4.关闭流
public class 序列化练习 { public static void main(String[] args) throws Exception{ String string = "张三,10,170;李四,12,173;王五,15,176"; String[] str = string.split("[,;]"); List<Persons> list = new ArrayList<Persons>(); for (int i = 0; i < str.length; i+=3) { Persons persons = new Persons(str[i],Integer.parseInt(str[1+i]),Double.parseDouble(str[2+i])); list.add(persons); } System.out.println(list); //序列化 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test01.txt")); objectOutputStream.writeObject(list); objectOutputStream.close(); //逆序列化 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test01.txt")); Object object = objectInputStream.readObject(); List<Persons> list2 = (List<Persons>) object; System.out.println(list2); objectInputStream.close(); } } class Persons implements Serializable{ String name; int age; double height; @Override public String toString() { return "Persons [name=" + name + ", age=" + age + ", height=" + height + "]"; } public Persons() { super(); // TODO Auto-generated constructor stub } public Persons(String name, int age, double height) { super(); this.name = name; this.age = age; this.height = height; } 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 double getHeight() { return height; } public void setHeight(double height) { this.height = height; } }
-
File类(demo14)
public static void main(String[] args) throws IOException {
/*
* 文件:File类,用来操作文件和路径(目录)
*
* 创建文件
* 创建路径
* 创建多路径
*
* 判断是否是文件
* 判断是否是路径
* 判断是否隐藏
* 获取最后修改文件的时间
*
* 获取根目录(路径)
* 获取指定目录下的文件或文件夹
*/
//创建File类的对象
//第一种方式:直接指定文件的绝对路径
File file1 = new File("D:\\workspace\\BigData1923N21\\src\\com\\qf\\test\\Demo1.java");
//第二种:通过父路径和子路径的字符串形式
File file2 = new File("D:\\workspace\\BigData1923N21","src\\com\\qf\\test\\Demo1.java");
//第三种:先得到父路径的对象形式,再跟子路径拼接
File file3 = new File("D:\\workspace\\BigData1923N21");
File file4 = new File(file3,"src\\com\\qf\\test\\Demo1.java");
// * 创建文件,在创建时,如果当前的文件已经存在了,不会覆盖
File file5 = new File("D:\\workspace\\BigData1923N21\\test1.txt");
file5.createNewFile();
// * 创建单层路径,只能创建单层路径,只能创建目录
File file6 = new File("D:\\workspace\\BigData1923N21\\a\\test2.txt");
file6.mkdir();
// * 创建多路径,也可创建单层目录,只能创建目录
file6.mkdirs();
// * 判断是否是文件
System.out.println(file6.isFile());//false
// * 判断是否是路径
System.out.println(file6.isDirectory());//true
// * 判断是否隐藏
System.out.println(file6.isHidden());//false
// * 获取最后修改文件的时间
long lastTime = file5.lastModified();
System.out.println(lastTime);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String value = simpleDateFormat.format(new Date(lastTime));
System.out.println(value);
}
demo16,demo17留作业
功能:作业:要求遍历当前目录下的所有文件----利用递归实现
实例:求1-10之间的偶数的积与奇数的积的差—利用递归
编码
- 编码:将字符串转化成byte序列的过程
- 解码:是将byte序列转成字符串的过程
- 编码错误:乱码:在执行读与写的时候,由于使用的字符集不同,造成了编码的错误.
中文乱码出现的情况研究:
- 但是如果是编码出错了,无法解决.如果是解码出错了,可以利用再编码再解码
- 编码 解码 结果
BK utf8 不可以(GBK2个字节,utf83个字节)
BK ISO8859-1 可以
tf8 GBK 有时可以
tf8 ISO8859-1 可以
SO8859-1 GBK 不可以(编码就出错了)
SO8859-1 utf8 不可以(编码就出错了)
- 编码 解码 结果
day01重点:装饰器模式(demo01),properties(demo12),序列化(demo13),file类(demo14)
day02
NIO
(jdk1.4开始)
Java NIO 由以下几个核心部分组成:
-
Channels:通道 Buffer:缓冲区 Selectors:选择器
-
Channels和Buffer是新IO中的两个核心对象,Channel是对传统的输入/输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输。
-
Channels与传统的InputStream,OutputStrem最大的区别在于它提供了一个map()方法,通过该map()方法可以直接将"一块数据"映射到
-
内存中,如果说传统的输入/输出系统是面向流处理,则新IO则是面向块的处理
-
Buffer可以被理解为一个容器(缓冲区,数组),发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须
-
放到Buffer中,也就是说数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中
使用Buffe
-
Buffer就像一个数组,它可以保存多个类型相同的数据,Buffer是一个抽象类其最常用的类是ByteBuffer,它可以在底层字节数组上进行
-
get/set操作,除了ByteBuffer之外,对应于其他基本数据类型(boolean除外)都有相应的Buffer类
NIO与IO的区别:
1.IO面向流,NIO面向缓冲区
-
Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。
-
此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。
-
Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。
-
这就增加了处理过程中的灵活性
2.IO是阻塞式的,NIO有非阻塞式的
-
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,
-
或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,
-
但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,
-
所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。
3.IO没有选择器,NIO有选择器
-
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,
-
然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。
-
这种选择机制,使得一个单独的线程很容易来管理多个通道
使用Buffer读写数据一般遵循以下四个步骤: 1. 写入数据到Buffer 2. 调flip()方法 3. 从Buffer中读取数据 4. 调用clear()方法或者compact()方法 当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到 读模式。在读模式下,可以读取之前写入到buffer的所有数据。 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。 clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处, 新写入的数据将放到缓冲区未读数据的后面。
NIO.2
- Java7对原有NIO进行了重大改进,改进主要包括如下两个方面的内容
- 1.提供了全面的文件IO和文件系统访问支持
- 2.基于异步Channel的IO
- Path、Paths和Files
-
- 之前学习中学习过File类来访问文件系统,但是File类的功能比较有限,NIO.2为了弥补这种不足,
- 引入了一个Path接口还提供了Files和Paths两个工具类,其中Files分装了包含大量静态方法来操作文件,
- Paths则包含了返回Path的静态工厂方法
网络通信
- 三大要素:IP , 端口号 ,协议
- IP:可以在网络中唯一的标记一台主机 -----------java中将IP面向对象了–InetAddress
- 端口:用来区分一台主机上的多个服务器(不可以重复) 取值范围:(0,65535) 注意点:在通信时两边的端口号要一致
- 网络协议:相当于指定的一个统一的标准
- URL:统一资源定位器
- URI:统一资源定位符
- URL的功能:可以通过它的方法直接获取网址的各个部分,还可以访问网络的资源 System.out.println(url.getProtocol());//协议
System.out.println(url.getPath());//资源路径System.out.println(url.getHost());//主机名/域名/IP地址
System.out.println(url.getQuery());//查询条件
System.out.println(url.getPort());//端口
System.out.println(url.getDefaultPort());//默认端口 443
//System.out.println(url.getContent());//查询的内容 URLConnection
代表客户端与服务器端的通信通道,这个类的实例可以用于读取或者写入url引入的资源
public class Demo3 {
public static void main(String[] args) throws IOException {
URL url = new URL("https://www.bingbing.com:333/a/b?name=bing&age=18");
System.out.println(url.getProtocol());//协议
System.out.println(url.getPath());//资源路径
System.out.println(url.getHost());//主机名/域名/IP地址
System.out.println(url.getQuery());//查询条件
System.out.println(url.getPort());//端口
System.out.println(url.getDefaultPort());//默认端口 443
//System.out.println(url.getContent());//查询的内容
URL url2 = new URL("https://www.baidu.com");
//System.out.println(url2.getContent());
//URLConnection代表客户端与服务器端的通信通道,这个类的实例可以用于读取或者写入url引入的资源
URLConnection connection = url2.openConnection();
//获取内部的字节输入流
InputStream inputStream = connection.getInputStream();
//进行读写
byte[] arr = new byte[1024];
inputStream.read(arr);
System.out.println(new String(arr,"utf8"));
}
}
UDP通信
模拟UDP通信
客户端
-
1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收)
-
2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据)
-
3.发送数据,通过send方法
-
4.关闭socket对象
//客户端 public class Demo6 { public static void main(String[] args) throws IOException { //* 1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收) DatagramSocket socket = new DatagramSocket(); // * 2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据) /* 打包 * 参数一:要传输的数据 * 参数二:数据的长度 * 参数三:服务器所在主机的地址 * 参数四:服务器的端口 */ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String data = null; while ((data = bufferedReader.readLine()) != null) { DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), InetAddress.getLocalHost(), 20000); //* 3.发送数据,通过send方法 socket.send(packet); //当输入over的时候结束 if (data.equals("over")) { break; } } //* 4.关闭socket对象 socket.close(); } }
服务器端:接收数据
1.创建socket对象,并绑定端口号—DatagramSocket
2.创建包对象,创建空数组,准备接收传来的数据 3.接收数据 4.关闭相关的对象
public class Demo7 {
public static void main(String[] args) throws IOException {
System.out.println("启动服务器成功,可以随时接收数据");
// * 1.创建socket对象,并绑定端口号---DatagramSocket
DatagramSocket socket = new DatagramSocket(20000);
//不断的接收数据
while (true) {
//* 2.创建包对象,创建空数组,准备接收传来的数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//* 3.接收数据:当服务器运行起来,让他一直处于监听状态的方法
socket.receive(packet);
//显示所有数据
byte[] bytes = packet.getData();
String data = new String(bytes);
System.out.println(new String(bytes));
//当输入over的时候结束
if (data.trim().equals("over")) {
break;
}
}
//* 4.关闭相关的对象
//socket.close();
}
}
TCP通信
-
TCP:
-
在客户端与服务器端通信的时候,对于客户端既要进行输入又要进行输出,所以在Socket对象的内部就内置了输入流和输出流,
-
当进行数据传输的时候,将数据放入socket对象的内部,将socket对象传到服务器端,相当于在客户端与服务器端建立了一个通道,
-
两端使用同一个socket对象.
//TCP的客户端
public class Demo8 {
public static void main(String[] args) throws IOException {
//1.创建Socket对象并绑定端口,管理服务器主机
Socket socket = new Socket(InetAddress.getLocalHost(), 30000);
//2.准备数据
String data = "BigData1924,你好";
//3.调用Socket内部的输出流向网络写数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(data.getBytes());
//3.使用socket的输入流将服务器回传的信息打印
InputStream inputStream = socket.getInputStream();
//将内容打印到控制台
byte[] arr = new byte[100];
int num = inputStream.read(arr);
String data1 = new String(arr,0,num);
System.out.println(data1);
socket.close();
}
}
//服务器端
public class Demo9 {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket对象并设置端口
ServerSocket serverSocket = new ServerSocket(30000);
//2.接收从客户端传来的socket对象
Socket socket = serverSocket.accept();
//3.使用socket的输入流卸货
InputStream inputStream = socket.getInputStream();
//将内容打印到控制台
byte[] arr = new byte[100];
int num = inputStream.read(arr);
String data = new String(arr,0,num);
System.out.println(data);
//给客户端回发一个信息,告知接收成功
//获取socket的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好,BigData1924".getBytes());
//4.关闭资源
serverSocket.close();
}
}
day03
反射
反射:动态获取类的字节码文件,并对其成员进行抽象
整体的含义:就是想通过字节码文件直接创建对象
过程:1.获取字节码文件对象,
- 先讲解获取字节码文件对象
//1.通过Object提供的getClass()方法 - 首先必须要有一个对象 XXX
//2.通过每种数据类型都有的一个class属性 - 在使用的位置必须当前的类是可见的,因为这里要显示的使用这个类名,对类的依赖性太强,使用不方便 XXX
//3.Class类提供的一个静态方法forName(字符串) 字符串:包名+类名 - 我们只需要提供一个当前类的字符串形式即可
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1.通过Object提供的getClass()方法
fun1();
//2.通过每种数据类型都有的一个class属性
fun2();
//3.Class类提供的一个静态方法forName(字符串) 字符串:包名+类名
fun3();
}
public static void fun1() {
Person person = new Person();
Class<?> class1 = person.getClass();
Class<?> class2 = person.getClass();
System.out.println(class1 == class2);//true
}
public static void fun2() {
Class<?> class1 = Person.class;
System.out.println(class1.getName());
}
public static void fun3() throws ClassNotFoundException {
//注意:要保证至少字符串对应的类是存在的
Class<?> class1 = Class.forName("com.qf.test.Person");
}
}
2.通过字节码文件对象获取对应的实列对象
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//普通方式
//Person person = new Person();
//通过反射创建普通对象
Class<?> class1 = Class.forName("com.qf.test.Person");
//方法一:通过无参的构造方法创建实例对象
fun1(class1);
//方法二:通过有参的构造方法创建实例对象
fun2(class1);
}
//方法一:通过无参的构造方法创建实例对象
public static void fun1(Class<?> cls) throws InstantiationException, IllegalAccessException {
//创建实例对象
//这里相当于在newInstance方法的内部调用了无参的构造方法
Object object = cls.newInstance();
Person person = (Person)object;
person.setName("bingbing");
System.out.println(person.getName());
}
//方法二:通过有参的构造方法创建实例对象
public static void fun2(Class<?> cls) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//先得到有参的构造方法
//这里要写参数的字节码文件对象形式 所有的类型都有字节码文件对象
//相当于 public Person(String name, int age)
Constructor constructor = cls.getConstructor(String.class,int.class);
Object object = constructor.newInstance("bingbing",18);
System.out.println(object);
}
}
3.给属性赋值(通过从属性中提取出来的类–Fileld)
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
Person person = new Person();
//person.name = "bingbing";
//使用反射实现
//1.获取字节码文件对象
Class<?> class1 = Class.forName("com.qf.test.Person");
//2.获取实例对象
Object object = class1.newInstance();
//3.调用属性
//注意:如果想使用getField,name属性必须是public的
//Field field1 = class1.getField("name");
//如果name是私有的,我们可以这样做 ,忽略权限
Field field1 = class1.getDeclaredField("name");
field1.setAccessible(true);
//赋值
//第一个参数:关联的具体对象
//第二个参数:赋的值
field1.set(object, "bing");
System.out.println(field1.get(object));
}
}
4.调用方法(通过从方法中提取出来的类)
public class Demo4 {
@Test
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//使用反射实现
//1.获取字节码文件对象
Class<?> class1 = Class.forName("com.qf.test.Person");
//调用非静态无参
fun1(class1);
//调用非静态有参
fun2(class1);
//调用静态有参
fun3(class1);
}
//调用非静态无参
public static void fun1(Class<?> cla) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//2.获取实例对象
Object object = cla.newInstance();
//3.通过反射得到方法
Method method = cla.getMethod("show");
//4.调用方法,通过调用invoke方法实现
method.invoke(object);
}
//调用非静态有参
public static void fun2(Class<?> cla) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//2.先得到有参的构造方法
Constructor<?> constructor = cla.getConstructor(String.class,int.class);
Object object = constructor.newInstance("bingibn",10);
//3.通过反射得到方法
Method method = cla.getMethod("callPhone",String.class);
//4.调用方法,通过调用invoke方法实现
method.invoke(object,"110");
}
//调用静态有参
public static void fun3(Class<?> cla) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//3.通过反射得到方法
Method method = cla.getMethod("run",int.class);
//4.调用方法,通过调用invoke方法实现
method.invoke(null,11);
}
}
静态代理
作用:可以实现代理
作用:根据OCP(对扩展开放,对修改关闭)的原则,在不改变原来类的基础上,给这个类增加额外的功能
缺点:代理对象要保证跟目标对象实现同样的接口,在维护的时候两个对象都要维护,
而且代理对象实现的接口是死的,这时如果要给想实现不同功能的多个目标对象添加代理对象的话,要添加很多个类
主要是代理类的编写(这里写的是找房子住的代理)
public class Agent implements TestInter{//TestInter中写的是找房子方法
//先给他个人
TestInter person;
public Agent(TestInter person) {
super();
this.person = person;
}
public void findHouse() {
System.out.println("扣一个月的房租作为中介费");
person.findHouse();
System.out.println("哈哈大笑");
}
}
动态代理
动态生成代理对象的方法–通过JDK内置的java.lang.reflect.Proxy动态代理类完成代理对象的创建
参数一:这里代表类加载器,代理类的类加载器要与目标类的类加载器一致,类加载器用来装载内存中的字节码文件
参数二:代理类与目标类实现的接口必须有相同的,即指定给代理类的接口,目标类必须实现了
参数三:代理类的构造方法生成的对象–注意:指定给构造方法的参数要使用Object
主要是代理类的编写
public class Test {
//模拟功能:bingbing和chenchen找房住
public static void main(String[] args) {
//静态代理
// Bingbing bingbing = new Bingbing();
// //bingbing.findHouse();
// Agent agent = new Agent(bingbing);
// agent.findHouse();
//动态代理
TestInter testInter = new Bingbing();
//调用动态代理的方法实现功能
/**
*动态生成代理对象的方法--通过JDK内置的java.lang.reflect.Proxy动态代理类完成代理对象的创建
*参数一:这里代表类加载器,代理类的类加载器要与目标类的类加载器一致,类加载器用来装载内存中的字节码文件
*参数二:代理类与目标类实现的接口必须有相同的,即指定给代理类的接口,目标类必须实现了
*参数三:代理类的构造方法生成的对象--注意:指定给构造方法的参数要使用Object
*
*/
// TestInter object = (TestInter)Proxy.newProxyInstance(testInter.getClass().getClassLoader(), new Class[] {TestInter.class}, new Agent(testInter));
//代理对象调动方法的时候,invoke方法会自动被调用
// object.findHouse();
//
// TestEat testEat = new Langlang();
// TestEat object1 = (TestEat)Proxy.newProxyInstance(testEat.getClass().getClassLoader(), new Class[] {TestInter.class,TestEat.class}, new Agent(testEat));
// object1.eat();
//进一步优化----直接使用InvocationHandler创建匿名内部类干活儿,不再需要Agent类
TestInter object2 = (TestInter)Proxy.newProxyInstance(testInter.getClass().getClassLoader(), new Class[] {TestInter.class},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("扣一个月的房租作为中介费");
Object object = method.invoke(testInter, args);
System.out.println("哈哈大笑");
return object;
}
}
);
object2.findHouse();
TestEat testEat = new Langlang();
TestEat object3 = (TestEat)Proxy.newProxyInstance(testEat.getClass().getClassLoader(), new Class[] {TestInter.class,TestEat.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("先洗后");
Object object = method.invoke(testEat, args);
System.out.println("哈哈大笑");
return object;
}
});
object3.eat();
//使用工厂方法
TestInter testInter3 = TestFactory.getAgentFactory(testInter);
testInter3.findHouse();
}
}
//创建一个工厂类---用于创建代理对象
public class TestFactory {
//工厂方法
public static TestInter getAgentFactory( final TestInter test) {
//final TestInter test = new Bingbing();
TestInter agent = (TestInter)Proxy.newProxyInstance(test.getClass().getClassLoader(), new Class[]{TestInter.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("扣一半房租做中介费");
// TODO Auto-generated method stub
//通过反射的方法调用具体对象的方法
Object object = method.invoke(test, args);
System.out.println("哈哈大笑");
return object;
}
});
return agent;
}
}
线程池(抽时间看)
ThreadPool(线程池)+CountDownLatch(程序计数器)
顾名思义,CountDownLatch为线程计数器,他的执行过程如下:
首先,在主线程中调用await()方法,主线程阻塞,
然后,将程序计数器作为参数传递给线程对象,
最后,每个线程执行完任务后,调用countDown()方法表示完成任务。
countDown()被执行多次后,主线程的await()会失效。
day04
mysql数据库
Sql****的分类
DDL(*)
(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等;
DML(**)
(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据);
DCL
(Data Control Language):数据控制语言,用来定义访问权限和安全级别;
DQL(*****)
(Data Query Language):数据查询语言,用来查询记录(数据)。
[外链图片转存失败(img-QYjdfhgr-1567949580326)(E:\web开发项目文件\Typora\1565408914561.png)]
day05
DDL:操作数据库、表、列等
一:对数据库的操作
创建
Createdatabase mydb1;
Create database mydb2 character setgbk;
Create database mydb3 character setgbk COLLATE gbk_chinese_ci;
查询
查看当前数据库服务器中的所有数据库
Show databases;
查看前面创建的mydb2数据库的定义信息
Show create database mydb2;
删除前面创建的mydb3数据库
Drop database mydb3;
修改
查看服务器中的数据库,并把mydb2的字符集修改为utf8;
alter database mydb2 character set utf8;
删除
Drop database mydb3;
其他:
查看当前使用的数据库
Select database();
切换数据库
Use mydb2;
二:操作数据表
当前数据库中的所有表 SHOWTABLES;
查看表的字段信息 DESCemployee;
在上面员工表的基本上增加一个image列。 ALTERTABLE employee ADD image blob;
修改job列,使其长度为60。 ALTERTABLE employee MODIFY job varchar(60);
删除image列,一次只能删一列。 ALTERTABLE employee DROP image;
表名改为user。 RENAMETABLE employee TO user;
查看表格的创建细节 SHOWCREATE TABLE user;
修改表的字符集为gbk ALTERTABLE user CHARACTER SET gbk;
列名name修改为username ALTERTABLE user CHANGE name username varchar(100);
备份表结构和表数据 create tabletname2 as select * from tname1;
备份表结构 create tabletname2 like tname1;
删除表 DROP TABLE user ;
DML操作:据操作语言,用来定义数据库记录(数据)
一:插入:
练习:
create table emp(
id int,
name varchar(100),
gender varchar(10),
birthday date,
salary float(10,2),
entry_date date,
resume text
);
INSERTINTO emp(id,name,gender,birthday,salary,entry_date,resume)
VALUES(1,‘zhangsan’,‘female’,‘1990-5-10’,10000,‘2015-5-5-’,‘goodgirl’);
INSERT INTOemp(id,name,gender,birthday,salary,entry_date,resume)
VALUES(2,‘lisi’,‘male’,‘1995-5-10’,10000,‘2015-5-5’,‘goodboy’);
INSERT INTOemp(id,name,gender,birthday,salary,entry_date,resume)
VALUES(3,‘你’,‘male’,‘1995-5-10’,10000,‘2015-5-5’,‘good boy’);
查看数据库编码的具体信息
Showvariables like ‘character%’;
二:修改:
语法:UPDATE 表名 SET 列名1=列值1,列名2=列值2 。。。 WHERE 列名=值
三:删除
语法: DELETE FROM 表名【WHERE 列名=值】
delete删除表中的数据,表结构还在,删除后的数据还可以找回
truncate 删除是把表直接DROP掉,然后再创建一个同样的新表。Truncate
删除的数据不能找回。执行速度比 delete 快。
DQL数据查询语言(重要)
查询返回的结果集是一张虚拟表
重要的sql语句:
一:分组查询
当需要分组查询时需要使用GROUPBY子句,例如查询每个部门的工资和,这说明要使用部门来分组。
//注:凡和聚合函数(组函数)同时出现的列名,则一定要写在groupby 之后
聚合函数(组函数也叫统计函数):
聚合函数是用来做纵向运算的函数:
COUNT():统计指定列不为NULL的记录行数;
MAX():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
MIN():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
SUM():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
AVG():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;
分组查询
: 查询每个部门的部门编号和每个部门的工资和:
SELECT deptno, SUM(sal)
FROM emp
GROUP BY deptno;
HAVING子句
: 查询工资总和大于9000的部门编号以及工资和:
SELECT deptno, SUM(sal)
FROM emp
GROUP BY deptno
HAVING SUM(sal) > 9000;
having与where的区别
1.having是在分组后对数据进行过滤
where是在分组前对数据进行过滤
- having后面可以使用分组函数(统计函数)
where后面不可以使用分组函数。
WHERE是对分组前记录的条件,如果某行记录没有满足WHERE子句的条件,那么这行记录不会参加分组;而HAVING是对分组后数据的约束。
LIMIT
LIMIT用来限定查询结果的起始行,以及总行数。
select * from emp limit 3,10;
3代表起始行为3; 10代表行数 (从第三行开始往下10行)
重要点:
1.使用统计查询时(存在GROUP BY子句),SELECT子句之中只允许出现统计函数与分组字段,其它的任何字段都不允许出现。
2.统计函数单独使用时(没有GROUP BY子句)只能够出现统计函数,不能够出现其它字段
3.此错误的意思就是where子句中不能使用组函数,因为where子句是在group by子句之前先执行的,所以在sql中要想给分组后的内容进行筛选则要依赖与having子句。
truncate 删除是把表直接DROP掉,然后再创建一个同样的新表。Truncate
删除的数据不能找回。执行速度比 delete 快。
DQL数据查询语言(重要)
查询返回的结果集是一张虚拟表
重要的sql语句:
一:分组查询
当需要分组查询时需要使用GROUPBY子句,例如查询每个部门的工资和,这说明要使用部门来分组。
//注:凡和聚合函数(组函数)同时出现的列名,则一定要写在groupby 之后
聚合函数(组函数也叫统计函数):
聚合函数是用来做纵向运算的函数:
COUNT():统计指定列不为NULL的记录行数;
MAX():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
MIN():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
SUM():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
AVG():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;
分组查询
: 查询每个部门的部门编号和每个部门的工资和:
SELECT deptno, SUM(sal)
FROM emp
GROUP BY deptno;
HAVING子句
: 查询工资总和大于9000的部门编号以及工资和:
SELECT deptno, SUM(sal)
FROM emp
GROUP BY deptno
HAVING SUM(sal) > 9000;
having与where的区别
1.having是在分组后对数据进行过滤
where是在分组前对数据进行过滤
- having后面可以使用分组函数(统计函数)
where后面不可以使用分组函数。
WHERE是对分组前记录的条件,如果某行记录没有满足WHERE子句的条件,那么这行记录不会参加分组;而HAVING是对分组后数据的约束。
LIMIT
LIMIT用来限定查询结果的起始行,以及总行数。
select * from emp limit 3,10;
3代表起始行为3; 10代表行数 (从第三行开始往下10行)
重要点:
1.使用统计查询时(存在GROUP BY子句),SELECT子句之中只允许出现统计函数与分组字段,其它的任何字段都不允许出现。
2.统计函数单独使用时(没有GROUP BY子句)只能够出现统计函数,不能够出现其它字段
3.此错误的意思就是where子句中不能使用组函数,因为where子句是在group by子句之前先执行的,所以在sql中要想给分组后的内容进行筛选则要依赖与having子句。