tcp和udp
TCP客户端使用步骤
-
1)创建客户端的Socket对象,指定ip和端口
-
2)获取客户端通道内容字节输出流对象,写数据
-
3)释放资源
TCP服务器端的代码实现
-
1)创建服务器端的Socket对象
-
2)监听客户端的连接
-
3)获取监听到的客户端的通道内的自节输入流对象,读数据
-
4)释放服务器端的资源
例题
TCP服务器端将复制客户端的文件内容,复制
public class TcpClientTest { public static void main(String[] args) throws IOException { //创建客户端的Socket对象 Socket s = new Socket("10.35.162.121",2222) ; //创建字符缓冲输入流对象 BufferedReader br = new BufferedReader( new FileReader("UdpReceive.java")) ; //获取客户端通道的字节输出流---->包装成BufferedWriter:字符缓冲输出流 BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(s.getOutputStream())) ; //每次从.java文件读取一行,给通道流的流中写一行 String line = null ; while((line=br.readLine())!=null){ bw.write(line) ; bw.newLine(); bw.flush(); } System.out.println("文件读完,发送过去了..."); //释放资源 br.close(); s.close(); } } public class TcpServerTest { public static void main(String[] args) throws IOException { //创建服务器端的Socket对象 ServerSocket ss = new ServerSocket(2222) ; //监听客户端连接 Socket s = ss.accept(); //获取监听客户端所在的通道内字节输入流对象---->包装成字符缓冲输入流 BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream())) ; //将监听客户端的通道内的字节流(已经被包装了字符缓冲输入流)的内容----通过的字符缓冲输出流写入到文件中 BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\EE_2211\\day27_code_resource\\copy.java")) ; //一次读取一行,写一行到文件中 String line = null ; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } System.out.println("复制完毕"); //释放资源 bw.close(); ss.close(); } }
问题解决:两个端都出现了互相等待
因为客户端的文本文件读完,是null作为结束条件,但是服务器端不断的从通道内的输入流去读数据,两端的通道不知道文件是否完了(服务器端不知道客户端的文件完没完),等待着写数据过来
解决方案:
通知服务器端,别等了,文件读完了
-
1)自定义结束条件 ,服务器端读到自定义结束条件,就反馈!
弊端:如果文件第一句话恰好是自定义的结束条件,就不好
-
2)推荐:在客户端这边有个终止通道内的流 没有数据写过去了!禁止输出流输出!
public void shutdownOutput() throws IOException
反射
什么是反射?
-
编译某个类的时候---->获取这个类的字节码文件,然后去加载,调用里面的成员方法,访问成员变量,通过构造方法创建对象!
编译过程
对这个类的属性/成员/构造其进行校验---->类加载器(负责类的加载过程)
BoostrapClassLoader:启动类加载器,负责java核心库,rt.jar
如何获取一个类的字节码文件?
-
任意Java对象的getClass方法
Person p = new Person() ; Class clazz = p.getClass(); System.out.println(clazz);
-
任意Java类型的class属性
Class clazz2 = Person.class ; System.out.println(clazz2);
-
java.lang.Class:代表正在运行的java类或者接口,通过静态方法
Class clazz3 = Class.forName("com.qf.reflect_06.Person");
通过反射方式获取类的字节码文件之后,创建当前类对象!
1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person") ;
2)获取类的Constructor所在的构造器对象
Constructor con = clazz.getConstructor();
3)通过它创建当前类实例
Object obj = con.newInstance(); System.out.println(obj);
在反射中如何调用成员方法
1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person") ;
2)通过无参构造器创建器Constructor创建当前类实例
如果你这个类没有提供任何构造方法(系统提供无参)或者无参构造方法是公共的---->此时可以直接使用Class类的newInstacne()创建当前类实例
Object obj = clazz.newInstance(); System.out.println(obj);
3)获取指定的成员方法所在的Method类对象
Method method = clazz.getMethod("show");//方法本身就是空参 System.out.println(method); method.invoke(obj) ;
4)反射调用
Method m1 = clazz.getDeclaredMethod("method", String.class);
//私有方法取消Java语言检查 m1.setAccessible(true) ; m1.invoke(obj,"hello,高圆圆") ; //第一个参数:当前类实例 //第二个参数:给方法的形式参数赋的实际参数 //调用方法到时候将指定的方法实际参数作用在当前类实例上
通过反射调用Person类中的私有的function方法
public class ReflectDemo { public static void main(String[] args) throws Exception { //1)获取当前类的字节码文件对象 Class clazz = Class.forName("com.qf.reflect_01.Person") ; //2)无参构造方法公共的,直接创建当前类实例 Object obj = clazz.newInstance(); //3)通过字节码文件对象获取当前类的成员方法类对象Method Method method = clazz.getDeclaredMethod("function"); //System.out.println(method); //4)抑制Java语言访问检查 method.setAccessible(true) ; //5)调用方法 Object returnObj = method.invoke(obj); System.out.println(returnObj); } }
通过反射获取这个类的成员变量所在的类对象Field,给成员变量赋值!
public class ReflectDemo2 { public static void main(String[] args) throws Exception{ //现在要通过反射获取成员变量的Field类对象并且去赋值 //1)获取当前类的字节码文件对象 Class clazz = Class.forName("com.qf.reflect_01.Person") ; //2)创建当前类实例 Object obj = clazz.newInstance() ; System.out.println(obj) ; //3)通过字节码文件对象获取成员变量的Field类对象 //获取公共的字段(成员变量)Field类对象 //public Field getField(String name)throws NoSuchFieldException,SecurityException //参数是属性名称 //获取指定的成员变量的Field类对象 //public Field getDeclaredField(String name)throws NoSuchFieldException,SecurityException //private String name ; //私有的成员变量 Field nameField = clazz.getDeclaredField("name"); //取消Java语言访问检查 nameField.setAccessible(true) ; //给成员变量的类对象Field赋值 //public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException //就是将指定的成员变量实际参数作用在指定实例上 //参数1:当前类实例 //参数2:成员变量的实际参数 nameField.set(obj,"高圆圆") ; System.out.println(obj); System.out.println("---------------------------------------------") ; //给age赋值 Field ageField = clazz.getField("age"); ageField.set(obj,44) ; System.out.println(obj) ; System.out.println("---------------------------------------------") ; //给gender赋值 Field genderField = clazz.getDeclaredField("gender"); genderField.setAccessible(true) ; genderField.set(obj,"女") ; System.out.println(obj); } }
ArrayList<Integer>通过反射给里面加入String类型的数据
public class ReflectTest { public static void main(String[] args) throws Exception{ //有一个ArrayList集合 ArrayList<Integer> array = new ArrayList<>() ; //当前类的实例 //泛型已经明确了数据类型 array.add(100) ; array.add(10) ; array.add(25) ; array.add(55) ; //array.add("helloworld") ; System.out.println(array); System.out.println("-------------------------------------") ; //获取字节码文件对象方式 :三种 //通过任意Java对象的getClass()获取到了当前正在运行的类字节码文件对象 Class clazz = array.getClass() ; //获取当前它的add方法的Method类对象 Method m = clazz.getMethod("add", Object.class); // System.out.println(m); //调用方法 m.invoke(array,"helloworld") ; m.invoke(array,"高圆圆") ; System.out.println(array); } }
通过修改配置文件实现不修改代码,而调用另一个类的方法
public class ReflectDemo { public static void main(String[] args) throws Exception { //原来的代码随着需求变化,可能需要用到不同类的某个的功能 //导致代码改来改去,不灵活 //设计原则---->"开闭原则" :对修改关闭,对扩展开放! // (对现有代码修改不好的,对现有带提供优化,扩展!) System.out.println("-------------------------------------------"); //1)在当期类中读取src下面的xx.properties InputStream inputStream = ReflectDemo.class. getClassLoader().getResourceAsStream("classname.properties"); //2)创建一个空的属性集合列表 Properties prop = new Properties() ; //System.out.println(prop); //3)加载资源文件输入流的内容到属性集合列表中 prop.load(inputStream); //System.out.println(prop);//{methodName=love, className=com.qf.reflect_02.Student} //4)通过属性集合列表中key获取value String className = prop.getProperty("className"); //System.out.println(className); String methodName = prop.getProperty("methodName") ; // System.out.println(methodName); //反射 //获取当前类的字节码文件对象 Class clazz = Class.forName(className) ; //创建当前类实例 无参构造方法系统提供 Object obj = clazz.newInstance() ; //通过字节码文件对象获取类的成员方法Method类对象 Method method = clazz.getMethod(methodName); //调用方法 method.invoke(obj) ; } }
代理设计模式
静态代理
代理角色和真实角色需要实现同一个接
动态代理
jdk动态代理
里面需要前提有一个接口
java.lang.reflect.Proxy提供了创建动态代理类和实例的静态方法 public static Object newProxyInstance( ClassLoader loader, //参数1:当前接口对象的类加载器 Class<?>[] interfaces, //参数2:代理实现接口列表字节码文件对象的数组 InvocationHandler h //参数3:代理实例 调用处理程序实现的接口 ) throws IllegalArgumentException
数据库的事务
在一个业务中执行多个sql(多张表的sql),这个sql语句要么同时执行成功,要么同时执行失败
mysql管理事务
start transaction:开启事务--在操作多个sql语句之前,将整个操作业务管理起来,自动提交切换手动提交事务
执行业务操作:多个sql语句或多张表的增删改,
如果出现问题,可以回滚--rollback
事务特点ACID(关系型数据库传统事务)
原子性,一致性,隔离性,持久性
原子性:在事务管理的时候,执行多个sqk增删改,要么同时成功,要么同时失败
一致性:高并发的时候,需要保证事务多次读写,保证数据一致性
隔离性:事务和事务是独立的,相互不影响
持久性:事务一旦提交,对数据的影响是永久性的,即使关机,数据还是要更改
事务隔离的级别
read uncommitted:读未提交
read committed:读已提交
repeatable read:可重复读
serializable:串行话
-
第一种级别read uncommitted:读未提交,会造成“脏读”,脏读是事务管理的最严重的问题:一个事务读取到,另一个没有提交的数据
-
第二种级别read committed:读已提交,为了防止脏读,出现一个问题“不可重复读”,事务多次读取的事务不一致
-
mysql的默认级别 repeatable read 可重复读,有效脏读,不可重复读,出现幻读!(一般有更新操作影响了数据)
-
最高级别:serializable:串行话 (一个事务读取到另一个没提交事务,数据查不到的,这个必须提交,才能操作数据!)
JDBC
连接数据库七大步骤
-
1)导包驱动包
-
2)注册驱动--加载驱动类
-
3)获取数据库的连接对象java.sql.Connection
-
4)准备sql语句
-
5)通过Connection连接对象获取数据库的执行对象
-
6)执行sql语句
-
7)释放资源
public class JdbcDemo { public static void main(String[] args) throws Exception { //Jdbc的操作:java连接数据库 //1)导入驱动jar包 //2)注册驱动---加载驱动类Class.forName("com.mysql.jdbc.Driver") ; //mysql5.5或者5.1的jar包:都是这个全限定名称 //mysql8.0jar包: com.mysql.cj.jdbc.Driver //3)获取数据库连接对象 //DriverManager驱动管理类---> //public static Connection getConnection(String url, 连接库地址 (统一资源定位符) // String user, mysql的用户名 root用户 // String password) 登录MySQL的密码 // throws SQLException //url--->组成: 协议名称 ://域名:端口号/具体的路径 /* * mysql的驱动jar包如果是8.0以后:url的后面 编码格式 是否启用证书登录 服务器时区 是否公钥模式 * jdbc:mysql://localhost:3306/ee_2211_02?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true * */ // jdbc:mysql://localhost:3306/库名 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/ee_2211_02", "root", "123456" ); //4)准备sql语句 String sql = "insert into account (name,balance) values('高圆圆',1000)" ; //5)通过数据库连接对象Connection获取数据库的执行对象 //执行静态sql语句 //Statement createStatement()throws SQLException创建一个Statement对象,用于将SQL语句发送到数据库 Statement stmt = conn.createStatement(); //6)执行sql语句 //Statement---> //int executeUpdate(String sql)throws SQLException 通用的更新操作 int count = stmt.executeUpdate(sql); System.out.println("影响了"+count+"行"); //7)释放资源 stmt.close(); conn.close(); } }