5_异常_多线程_设计模式_IO流_网络编程_反射
JavaSE_第五周
异常
异常的概念
什么是异常
概念
概念:程序在运行过程中出现的特殊情况异常-----通过Jvm将异常的信息打印在控制台---告诉开发者(当前程序在某个环节出现了哪些问题!)
异常处理的必要性
异常处理的必要性:任何程序都可能存在大量的未知问题、错误,如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。
异常的分类
Throwable
Throwable: 可抛出的,一切错误或异常的父类,位于java.lang包中
Error
Error: JVM,硬件,执行逻辑错误,不能手动处理例:Error:StackOverflowError 堆栈溢出错误
Exception
Exception: 程序在运行和配置中产生的问题,可处理
RuntimeException
RuntimeException:程序在执行过程中产生的异常,可处理,可不处理
CheckedException
CheckedException:受查异常,也称编译时期异常,Java语法原因,导致出现的问题,必须处理
异常的产生
自动抛出异常
自动抛出异常:当程序在运行时遇到不规范的代码和结果时,会产生异常
手动抛出异常
手动抛出异常:语法:throw new 异常类型(实际参数);
产生异常结果
产生异常结果:相当于遇到return语句,导致程序因异常而终止
异常的传递
异常的传递
异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)
受查异常
编译时期异常:语法通过不了,或者使用jdk提供的一些本身就带有异常的方法,不处理不行需要开发者要显示处理,否则报错!受查异常:throws 声明异常,修饰在方法参数列表后端
运行时异常
运行时期异常:开发者可以显示处理,也可以不显示处理,无需声明异常,可以通过逻辑代码判断...
public static void method2() {//显示处理/* try{int a = 20 ;int b = 0 ;System.out.println(a/b);}catch(Exception e){System.out.println("除数不能为0");}*/int a = 20 ;int b = 0 ;//代码逻辑判断if(b!=0){System.out.println(a/b);}else{System.out.println("除数为0!");}}
常见的的运行时期异常
public class TestRuntimeException {public static void main(String[] args) {m6();}//java.lang.NullPointerExceptionpublic static void m1() {Object o = null;o.hashCode();}//java.lang.ArrayIndexOutOfBoundsExceptionpublic static void m2() {int[] nums = new int[4];System.out.println(nums[4]);}//java.lang.StringIndexOutOfBoundsExceptionpublic static void m3() {String str = "abc";System.out.println(str.charAt(3));}//java.lang.ArithmeticExceptionpublic static void m4() {System.out.println(3/0);}//java.lang.ClassCastExceptionpublic static void m5() {Object o = new Integer(688);String s = (String)o;}//java.lang.NumberFormatExceptionpublic static void m6() {new Integer("10A");}
}
JVM的默认处理方案
把异常的名称,错误原因及异常出现的位置等信息输出在了控制台 ,程序停止执行
异常的处理
捕获异常
try{可能出现异常的代码
}catch(Exception e){异常处理的相关代码,如:getMessage(); printStackTrace()
}finally{无论是否出现异常,都需执行的代码结构,常用于释放资源
}try{int result = num1 / num2;//throw new ArithmeticException("/by zero")System.out.println(result);
}catch(Exception e) {// new ArithmeticException();
// System.out.println("除数不能为零!");//处理方式1(自定义处理)
// e.printStackTrace();//处理方式2(打印堆栈跟踪信息)System.out.println(e.getMessage());//处理方式3(打印throwable中详细消息字符串)
}如果try不存在问题,最终try里面的代码执行完毕,执行finally代码;
finally中代码一定会执行,除非 jvm退出了!finally语句中代码:释放系统资源数据库连接对象.close();IO流中的流对象.close() ;jdbc执行对象Statement.close()获取数据结果集对象.close()...
public class ExceptionDemo4 {public static void main(String[] args) {try{//给定一个日期文本格式String source = "2021-2-23" ;//创建SimpleDateFormat对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd "); //存在问题:模式不匹配//解析Date date = sdf.parse(source) ; //jvm执行:内存中:创建ParseException实例System.out.println(date);}catch (ParseException e){System.out.println("文本解析出现问题了...");//jvm退出System.exit(0) ;//0:正常终止jvm}finally{System.out.println("这里面的代码一定会执行...");}System.out.println("程序over...");}
}
抛出异常
throws :抛出,消极处理(告知了调用者,此方法可能会产生异常)
throws和throw的区别
throws和throw的区别1)throws: 位置在方法声明上throw:位置是在方法体中2)throws的后面跟的是异常类名,后面可以跟多个异常类名,中间使用逗号隔开throw的后面跟的是异常对象,一般情况 new XXXException() ; 后面跟的是某个具体的异常对象3)throws表示的是抛出异常的一种可能性!(不一定)throw:表示抛出异常的一种肯定性,执行这段代码,一会产生这个异常!4)throws抛出异常,调用者必须对当前这个方法中的异常进行处理throw抛出异常,方法体中通过逻辑代码控制!
public class ExceptionDemo2 {public static void main(String[] args) {System.out.println("程序开始了...");//捕获处理try {method1() ;} catch (ParseException e) {e.printStackTrace();}System.out.println("程序结束了...");// method2() ;}//throwprivate static void method2() {// 定义两个变量int a = 10 ;int b = 0 ;if(b!=0){System.out.println(a/b);}else{//代码执行此处,抛出异常throw new ArithmeticException() ; //创建异常实例(匿名对象)}}//解析日期文本格式private static void method1() throws ParseException { //抛出异常String s = "2022-6-30" ;//String--->Date格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;//解析Date date = sdf.parse(s) ; //日期格式System.out.println("date格式为:"+date);}
}
Throwable中的常用的功能
关于Throwable中的一些常用的功能public String getMessage():获取异常的详细消息字符串public String toString():获取异常的简单描述异常类名: 详细的消息字符串getMessage()public void printStackTrace():追踪错误的输出流(包含异常的toString()) (推荐)
public class ExceptionDemo3 {public static void main(String[] args) {try{//给定一个日期文本格式String source = "2021-2-23" ;//创建SimpleDateFormat对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //存在问题:模式不匹配//解析Date date = sdf.parse(source) ; //jvm执行:内存中:创建ParseException实例System.out.println(date);}catch (ParseException e){//使用throwable 的功能//执行catch语句// String msgStr = e.getMessage() ;// System.out.println(msgStr);// public String toString()//String msgStr = e.toString() ;//System.out.println(msgStr);//public void printStackTrace():e.printStackTrace();}System.out.println("程序over...");}
}
自定义异常
概念
自定义异常:需继承自Exception或Exception的子类,常用RuntimeException
构造方法
无参构造方法
有String message参数的构造方法
public class TestDefinedException {public static void main(String[] args) {Student s = new Student();try {s.setAge(333);} catch (AgeException e) {e.printStackTrace();//打印堆栈跟踪信息}try {s.setSex("666");}catch(SexMismatchException e){System.err.println("性别输入有误," + e.getMessage());}}}//受查异常
class AgeException extends Exception{public AgeException() {super();}public AgeException(String message) {super(message);}}
//运行时异常
class SexMismatchException extends RuntimeException{public SexMismatchException() {super();}public SexMismatchException(String message) {super(message);}}class Student{private int age;private String sex;public int getAge() {return age;}public void setAge(int age) throws AgeException {if(age > 0 && age <= 253) {this.age = age;}else {//抛出异常throw new AgeException("年龄的正确取值区间:1~253");}}public String getSex() {return sex;}public void setSex(String sex) {if(sex.equals("男") || sex.equals("女")) {this.sex = sex;}else {//抛出异常throw new SexMismatchException("性别的正确取值:'男' 或 '女'");}}}
方法重写
带有异常声明的方法覆盖:方法名、参数列表、返回值类型必须与父类相同子类的访问修饰符和父类相同,或比父类更宽泛子类中的方法,不能抛出比父类或接口 更宽泛的异常
public class TestOverrideExceptionMethod {public static void main(String[] args) {Super s = new Sub();try {s.method();} catch (ClassCastException e) {e.printStackTrace();}Printable p = new MyClass();try {p.print();} catch (IOException e) {e.printStackTrace();}MyClass mc = new MyClass();mc.print();}}class Super{public void method() throws ClassCastException {System.out.println("Super---method()");}
}class Sub extends Super{public void method() {System.out.println("Sub---method()");}
}interface Printable{public void print() throws IOException;
}class MyClass implements Printable{@Overridepublic void print(){System.out.println("MyClass -- print()");}}
面试题
//1.打印输出结果
public class TestTryCatchFinally {public static void main(String[] args) {System.out.println(method(4));}public static int method(int n) {try {if(n % 2 == 0) {throw new RuntimeException("不能为偶数");}return 10;} catch (Exception e) {System.out.println("捕获异常...");return 20;}finally {System.out.println("方法结束");//必须执行}}}运行结果:捕获异常...方法结束202.打印输出结果
public class FinallyTest {public static void main(String[] args) {System.out.println(getNum(20));//结果?}private static int getNum(int a) {System.out.println(a);//20try{a = 10 ;System.out.println(a/0);}catch (ArithmeticException e){ //执行catcha = 30 ; // 30return a ; //形式返回路径了: return 30}finally{a = 40 ;}return a ;}
}运行结果:30如果处理异常的时候,try...catch...finally格式,catch语句中有return语句, finally中代 码还会执行吗?如果会执行,是在catch之前还是之后?finally会执行,具体在finally之前(中间),return 变量:已经形成返回路径!finally代码一定会执行,除非是在执行finally语句之前,jvm退出了!3. finally,final,finalize的区别finally:捕获异常的finally语句, try...catch...finally一般用途就是释放相关的资源(连接对象资源,流资源,执行(jdbc)资源)finally中的代码一定会执行,在执行finally之前,如果jvm退出了,finally语句是不会执行的!
fiinal,状态修饰符修饰类 :该类不能继承修饰成员变量 :此时是一个常量修饰成员方法 :该方法不能重写
finalize:Object类中的方法,开启垃圾回收器:System.gc()---->实际调用的finalize()回收没有更多引用的对象!4.处理异常的方式有几种
捕获异常 try...catch...finallytry...catch...catch...
抛出异常throws:抛出在方法上throw:在方法体中抛出5.throw和throws的区别throws: 1)在方法声明上使用2)throws后面跟的异常类名,中间使用逗号隔开可以跟多个异常类名 (为了使用方便)3)在当前方法上如果使用throws,调用者必须对这个方法处理!4)throws表示出现异常的可能性!throw:1)方法语句体中使用2)后面跟的异常对象,一般匿名对象(具体异常具体抛出) throw new xxxException() ;3)它对异常的处理,是通过逻辑代码进行判断4)throw表示出现异常的肯定性!
多线程
什么是线程
进程
进程:正在运行的应用程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间 和系统资源。
程序是静止的,只有真正运行时的程序才能被称为进程单核CPU在任何时间点上,只能运行一个进程,宏观并行,微观串行多进程---为了提高CPU的使用率!同时玩游戏,听音乐,并不是同时进行,而是CPU的一点点时间片在
多个进程之前进行高效的切换!(和CPU有关系)
线程
线程(Thread) 一个轻量级的进程(是最小的基本单元) -----进程的某个任务
程序的一个顺序控制流程,是CPU的基本调度单位。
多线程
多个线程组成,彼此间完后不同的工作,交替执行,成为多线程多个线程在互相抢占CPU的执行权,执行具有随机性
Java程序运行原理
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主 线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
JVM是多线程吗
Jvm,是一个假想计算机,是一个进程,是一个多线程环境,默认包含主线程(main)
通过代码创建多个独立的线程,与main并发执行不断的创建对象------ 需要使用垃圾回收器回收,------垃圾回收线程至少存在两条线程: main和垃圾回收线程(开启垃圾回收器)
进程与线程之间的区别
进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
一个程序运行后至少有一个进程
一个进程可以包含多个线程,但是至少需要一个线程
进程之间不能共享数据段地址,但同进程的线程之间可以
线程的组成
任何一个线程都具有基本的组成部分:CPU时间片:操作系统(os)会为每个线程分配执行时间运行数据:堆空间:存储线程需使用的对象,多个线程可以共享堆中的空间栈空间;存储线程需使用的局部变量,每个线程都拥有独立的栈线程的逻辑代码
多线程的实现
使用Java程序如何实现多线程?线程----->依赖于进程---->需要有一个进程----->创建进程------需要创建系统资源Java语言不能创建系统资源----创建系统资源---->使用C/C++提供了一个类-----> java.lang.Thread:线程是程序中的执行线程!jvm运行多个线程并发的执行!
多线程环境的实现方式
继承Thread类
继承关系(1)自定义一个类,继承自Thread类(2)重写Thread类中的run方法(3)在main线程中,创建该类对象(4)启动线程(start())
实现Runnable接口
实现Runnable接口的方式(1)自定义类实现Runnable接口(2)重写Runnable接口中的run 方法(3)创建该类对象-----资源类(可以被多个线程共用)创建Thread类对象将资源类作为参数进行传递Thread(Runnable target,String name)(4)启动线程接口实现关系----(面向接口编程---- 多个线程对象在操作同一个资源:共享的概念) 里面用到代理模式之静态代理代理模式静态代理动态代理1)jdk动态代理---基于接口实现(InvocationHandler)2)cglib动态代理----基于子类实现
线程池
线程池1)实现Runnable接口自定义类实现Runnable接口重写Runnable接口中的run方法使用工厂类(Executors)创建线程池对象(newFixedThreadPool)提交异步任务(Future<?> submit(Runnable task))关闭线程池(shutdown())2)实现Callable接口自定义类实现Callable接口重写Calllable接口中的call方法使用工厂类(Executors)创建线程池对象(newFixedThreadPool)提交异步任务(<T> Future<T> submit(Callable<T> task))获取结果(V get())关闭线程池(shutdown())
Java能够开启多线程吗?
Java是不能够开启的,借助于底层语言开启多线程环境(根据系统资源)start()private native void start0(); 非Java语言实现(native:本地方法)
使用匿名内部类的方式实现
public class ThreadDemo {public static void main(String[] args) {//方式1new Thread(){@Overridepublic void run() {for(int x = 0 ; x < 100 ; x ++){System.out.println(getName()+":"+x);}}}.start();//方式2://自定义类一个---实现Runnable接口----> 资源类new Thread(new Runnable(){@Overridepublic void run() {for(int x = 0 ; x < 100 ; x ++){System.out.println(Thread.currentThread().getName()+":"+x);}}}).start();System.out.println("-----------------------------");//内部类(有名字类)----->匿名内部类----> 前提:如果一个接口中只有一个抽象方法,这个接口@FunctionalInterface:函数式接口//jdk8以后---拉姆达表达式//企业中:jdk8新特性使用到./jdk7/jdk5//要求:在开发中使用枚举类/拉姆达}
}
Thread类(java.lang.Thread)
构造方法
public Thread():无参构造public Thread(Runnable target):有参构造:里面传递Runnable接口 (间接使用就 静态代理)
成员方法
线程名称
public final void setName(String name):设置线程名称public final String getName():获取线程名称
线程调度
线程有两种调度模型:
分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级 高的线程获 取的 CPU 时间片相对多一些。 Java使用的是抢占式调度模型。
线程优先级
public final int getPriority():获取线程的优先级默认的优先级:5
public final void setPriority(int newPriority):更改优先级优先级的常量表
public static final int MAX_PRIORITY 10 最高优先级
public static final int MIN_PRIORITY 1 最低优先级
public static final int NORM_PRIORITY 5 默认优先级优先级越大的----当前这个线程抢占到的CPU执行权越大
守护线程
public final void setDaemon(boolean on)该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。这个方法调用必须在启动之前调用! (注意事项)public static Thread currentThread()返回正在执行的线程对象的引用
public class ThreadDaemonDemo {public static void main(String[] args) {//创建线程类对象ThreadDeamon td1 = new ThreadDeamon() ;ThreadDeamon td2 = new ThreadDeamon() ;//设置线程名称td1.setName("关羽");td2.setName("张飞");//启动线程之前,将td1,td2标记为守护线程 ,jvm会自动退出td1.setDaemon(true);td2.setDaemon(true);//执行两个子线程td1.start();td2.start();Thread.currentThread().setName("刘备");for(int x = 0 ; x <10 ; x ++){//public static Thread currentThread()//获取当前正在执行的线程mainSystem.out.println(Thread.currentThread().getName()+":"+x);}}}
public class ThreadDeamon extends Thread {//td1,td2子线程@Overridepublic void run() {for(int x = 0 ; x < 100; x ++){System.out.println(getName()+":"+x);}}
}
线程休眠
public static void sleep(long millis) throws InterruptedException:线程休眠 ,参数为毫秒值线程休眠----时间休眠期到了,又继续去执行线程! (属于阻塞式方法)
线程放弃
public static void yield() :暂停当前正在执行的线程对象,并执行其他线程当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
线程结合
public final void join()throws InterruptedException等待该线程终止。允许其他线程加入到当前线程中,并优先执行底层依赖于 wait(long mills)非Java语言 (线程阻塞)
线程中断
public final void stop()让线程停止,过时了,但是还可以使用。
public void interrupt()中断线程。 把线程的状态终止,并抛出一个InterruptedException。
线程的状态
线程的状态有六种:
NEW---初始状态
RUNNABLE---运行状态
BLOCKED---阻塞状态
WAITING---无限期等待
TIMED_WAITING---限期等待
TERMINATED---终止状态注:JDK5之前为7种状态,JDK5之后就绪(Ready)、运行(Running)统称为Runnable
线程安全
线程不安全
线程不安全:当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可打乱或缺省
校验多线程安全问题的标准
校验多线程安全问题的标准1)当前是否是一个多线程环境 2)是否存在共享数据 3)是否有多条语句对共享数据操作
解决多线程安全问题
基本思想:让程序没有安全问题的环境。
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可使用synchronized(锁对象){ //多个线程必须使用的是同一把锁(钥匙)多条语句对共享数据操作}
线程同步
同步机制
synchronized基于jvm,------->多个线程持有"锁标志",通过同步代码块控制访问的字段,每一个持有的锁必须是同一个,当某个线程如果执行了并且进入到同步代码块中,其他线程在当前线程执行期间,不能够持有该锁,
当前这个线程执行完毕,会释放"锁标志";其他线程如果进入到同步代码块中,持有该同一个"锁"synchronized和Lock都属于可重入锁,Lock锁更灵活: lock()/unlock(),通用方式:synchronizedsynchronized(锁对象){语句体(多条语句多共享数据的操作)}
同步方式1
同步方式:同步代码块:synchronized(临界资源对象){//对临界资源对象加锁//代码(原子操作)}注:每一个对象都有一个互斥锁标记,用来分配给线程的只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的的同步代码块线程退出同步代码块时,会释放相应的互斥锁标记
同步方式2
同步方法:synchronized 返回值类型 方法名称 (形参列表){ // 对当前对象(this)加锁//代码(原子操作)}注:只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中线程退出同步方法时,会释放相应的互斥锁标记
同步的规则
同步规则:注意:只有在调用包含同步代码块的方法或同步方法时,才需要对象的锁标记如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用
同步方法的锁对象
同步方法 ---->这里面的锁对象是谁? this:代表当前类对象的地址值引用 //锁对象可以是任意的java类对象
静态的同步方法?它的锁对象是谁? 锁对象:当前类的字节码文件对象---"反射思想"类名.class
已知线程安全的类
已知JDK中线程安全的类StringBufferVectorHashtable以上类中的公开方法,均为synchronized修饰的同步方法
线程的同步和异步
线程的同步:形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续线程的异步:形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行
LOCK接口
JDK5以后提供了一比Synchronized更广泛的锁定操作.也可以实现锁对象控制多个线程对共享资源的访问!
java.util.concurrent.locks.Lock:接口
与synchronized比较
lock与synchronized比较,显示定义,结构更灵活
提供更多实用性方法,功能更强大,性能更优越
常用方法
void lock() //获取锁,如锁被占用,则等待
boolean tryLock() //尝试获取锁(成功返回true,失败返回false,不阻塞)
void unlock() //释放锁
子实现类
ReentrantLock
重入锁:ReentrantLock: Lock接口的实现类,与synchronized一样具有互斥锁功能
//银行取款案例
public class TestReentrantLock {public static void main(String[] args) throws InterruptedException {Account acc = new Account("1001","123456",2000D);Husband h = new Husband(acc);Wife w = new Wife(acc);Thread t1 = new Thread(h);Thread t2 = new Thread(w);t1.start();t2.start();}}class Husband implements Runnable{Account acc;public Husband(Account acc) {this.acc = acc;}@Overridepublic void run() {this.acc.withdrawal("1001", "123456", 1200D);}
}class Wife implements Runnable{Account acc;public Wife(Account acc) {this.acc = acc;}@Overridepublic void run() {this.acc.withdrawal("1001", "123456", 1200D);}
}class Account{Lock locker = new ReentrantLock();String cardNo;String password;double balance;public Account(String cardNo, String password, double balance) {super();this.cardNo = cardNo;this.password = password;this.balance = balance;}public void withdrawal(String no,String pwd,double money) {//开启锁 synchronized(this){locker.lock();try {System.out.println("请稍后。。。");if(this.cardNo.equals(no) && this.password.equals(pwd)) {System.out.println("验证成功,请稍后。。。");if(money < balance) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}balance -= money;System.out.println("取款成功,当前余额为:" + balance);}else {System.out.println("卡内余额不足!");}}else {System.out.println("卡号或密码错误!");}}finally {//释放锁 }locker.unlock();}}
}
ReentrantReadWriteLock
读写锁:ReentrantReadWriteLock:一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁支持多次分配读锁,使多个读操作可以并发执行互斥规则:写——写:互斥,阻塞读——写:互斥,读阻写塞,写阻读塞读——读:不互斥,不阻塞在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率
public class TestReentrantReadWriteLock {public static void main(String[] args) {final Student s = new Student();Callable<Object> writeTask = new Callable<Object>() {@Overridepublic Object call() throws Exception {s.setValue(111);return null;}};Callable<Object> readTask = new Callable<Object>() {@Overridepublic Object call() throws Exception {s.getValue();return null;}};ExecutorService es = Executors.newFixedThreadPool(20);long start = System.currentTimeMillis();for(int i = 0; i < 2; i++) {es.submit(writeTask);}for(int i = 0; i < 18; i++) {es.submit(readTask);}//停止线程池(不再接受新任务,将现有任务全部执行完完毕)es.shutdown();while(true) {System.out.println("结束了吗?");if(es.isTerminated()) {System.out.println("终于结束了");break;}}//....System.out.println(System.currentTimeMillis() - start);}}class Student{// ReentrantLock rLock = new ReentrantLock();ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();ReadLock rl = rwl.readLock();WriteLock wl = rwl.writeLock();int value;//读public int getValue() {rl.lock();try {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return this.value;}finally {rl.unlock();}}//写public void setValue(int value) {wl.lock();try {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}this.value = value;}finally {wl.unlock();}}}
线程间的通信
线程的通信:等待:public final void wait()public final void wait(long timeout)必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有的锁标记,同时此线程因obj处在无限期等待的状态中,释放锁,进入等待队列。通知:public final void notify()public final void notifyAll()必须在对obj加锁的同步代码块中,从obj的waiting中释放一个或全部线程,对自身没有任何影响
等待唤醒机制
多个线程在使用资源的时候,如果当前资源并非同一个资源,可能会出现锁;
引入生产者消费者模式思想: (操作必须同一个资源对象!)生产者线程----不断的产生数据----如果当前没有数据了,等待生产者线程产生数据当前这个数据已经有了,需要通知(notify())消费者线程,使用这个数据消费者线程----不断的使用数据----如果当前有数据了,等待使用完毕如果没有数据了,通知(notify())生产者线程---产生数据....
sleep()和wait()的区别
1)是否会释放锁sleep()的调用,不会释放锁---->通过Thread类的访问的wait()的调用,会立即释放持有的"锁标志"---->通过锁对象访问的
2)来源不同sleep()来源于Thread类wait()----Object
3)都可能会有异常中断异常sleep() throws InterruptedException{}wait() throws InterruptedException{}
为什么wait(),notify(),notifyAll()等方法都定义在Object类中
1,这些方法存在与同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
静态代理
Thread类本身底层使用的就是静态代理什么是静态代理通过代理角色帮助真实角色完成业务,对真实角色业务进行增强!开发中,打印日志/事务管理/权限校验....真实角色(目标角色):专注于自己的事情代理角色:帮助真实角色进行方法增强!特点:真实角色和代理角色都需要实现同一个接口!动态代理jdk动态代理:通过反射方式---基于接口第三方jar包:cglib动态代理----基于子类实现
/*** Thread类本身底层使用的就是静态代理** 什么是静态代理* 通过代理角色帮助真实角色完成业务,对真实角色业务进行增强!* 开发中,打印日志/事务管理/权限校验....** 真实角色(目标角色):专注于自己的事情* 代理角色:帮助真实角色进行方法增强!* 特点:* 真实角色和代理角色都需要实现同一个接口!*** 举例:* 结婚这件事情** 1)自己完成结婚这件事情(真实角色)* 2)加入一个代理角色:婚庆公司,帮助我们完成结婚这件事情! WeddingCompany**** //Thread:就是代理角色* //MyRunnable:真实角色---->run()---> 业务操作....***** 反射---------->动态代理* jdk动态代理:通过反射方式---基于接口* 第三方jar包:cglib动态代理----基于子类实现**///测试类
public class ThreadDemo {public static void main(String[] args) {//测试//接口多态的方式
// Marry marry = new You() ;//marray.happyMarry();//具体类You you = new You() ;you.happyMarry();System.out.println("--------------------------------------------------");//创建婚庆公司类对象:代理角色//创建真实角色对象You you2 = new You() ;WeddingCompany wc = new WeddingCompany(you2) ;wc.happyMarry(); //对真实角色You类型中happyMarry()进行了增强//对比Thread类//创建线程的实现方式2: 自定义类//class MyRunnable implement Runnable{// public void run(){//// ...// }// }/**** 源码:* public class Thread implements Runnable{** private Runnable target; //Runnable接口类型*** public Thread(Runnable target,String name) {** ....* }* @Override* public void run() {* if (target != null) { //如果当前Runnable接口对象(通过子实现类实例化)* target.run();* }* }* }*/}
}//定义接口:Marry:结婚
interface Marry{//结婚void happyMarry() ;
}//定义类:You----- 真实角色
class You implements Marry{@Overridepublic void happyMarry() {System.out.println("很开心,要结婚了...");}
}//定义一个代理角色:目的就是happyMarry(),进行增强
class WeddingCompany implements Marry{//代理角色也实现该接口//声明一个真实角色类型:Youprivate You you ;//构造方法 // Thread t1 = new Thread(自定义的类实现Runnbale接口的对象) ;//代理public WeddingCompany(You you){this.you = you ;}@Overridepublic void happyMarry() {//结婚之前before() ;//真实角色:专注于自己的事情:结婚you.happyMarry(); //结婚//结婚之后after() ;}public void before() {System.out.println("结婚之前,布置婚礼现场...");}public void after() {System.out.println("结婚之后,男方付尾款...");}}
经典问题
死锁
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁死锁现象:当前多个线程操作的资源对象,不是同一个对象,而且加入同步代码块之后,出现了一种互相等待的情况
public class ThreadDemo {public static void main(String[] args) {//创建资源类对象DieLockDieLock d1 = new DieLock(true) ;DieLock d2 = new DieLock(false) ;//创建线程类对象Thread t1 = new Thread(d1) ;Thread t2 = new Thread(d2) ;//分别启动线程t1.start();t2.start();/*** 情况1:* if objA t1* else objB t2** 情况2:* else objB t2先抢占到* if objA*** 理想状态:* else objB t2抢占到* else objA* if objA t1执行* if objB*/}
}
//自定义类中,创建两把锁对象
public class MyDieLock {public static final Object objA = new Object() ;public static final Object objB = new Object() ;
}//资源类
public class DieLock implements Runnable{//声明一个boolean类型的变量private boolean flag ;public DieLock(boolean flag){this.flag = flag ;}@Overridepublic void run() {//加入判断if(flag){//truesynchronized (MyDieLock.objA){ //objA锁System.out.println("if objA"); //if ObjAsynchronized (MyDieLock.objB){System.out.println("if objB");}}}else{//falsesynchronized (MyDieLock.objB){ //t2System.out.println("else objB"); //else objBsynchronized(MyDieLock.objA){System.out.println("else objA");}}}}
}
生产者与消费者
生产者与消费者问题是多线程同步的一个经典问题。生产者和消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区中取出商品。我们需要保证的是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。
public class TestPAC {public static void main(String[] args) {final MyStack ms = new MyStack();
// ms.push("A");
// ms.push("B");
// ms.push("C");
// ms.push("D");
// ms.push("E");
// ms.push("E");
//
// ms.pop();
// ms.pop();
// ms.pop();
// ms.pop();
// ms.pop();
// ms.pop();Thread t1 = new Thread() {public void run() {for(char ch = 'A'; ch <= 'Z'; ch++) {ms.push(ch + "");}}};Thread t2 = new Thread() {public void run() {for(int i = 0; i < 26; i++) {ms.pop();}}};t1.start();t2.start();}}class MyStack{private String[] values = new String[] {"","","","",""};int size = 0;public synchronized void push(String str) {this.notify();//唤醒对方while (values.length == size) {System.out.println("满了");try { this.wait();} catch (Exception e) {}//结束 ---> 暂停(等待)}System.out.println(str + "入栈");values[size] = str;size++;}public synchronized void pop() {this.notify();//唤醒对方while (size == 0) {System.out.println("空了");try {this.wait();} catch (Exception e) {}//结束 ---> 暂停(等待)}System.out.println(values[size-1] + "出栈");values[size-1] = "";size--;}
}
import java.util.ArrayList;
import java.util.List;public class TestProducerAndConsumer {public static void main(String[] args) {MyQueue mq = new MyQueue();// mq.offer("A");
// mq.offer("B");
// mq.offer("C");
// mq.offer("D");
// mq.offer("E");
//
// System.out.println(mq.poll());
// System.out.println("以下是遍历内容:");
//
// mq.show();Produce1 p1 = new Produce1(mq);Produce2 p2 = new Produce2(mq);Consumer c1 = new Consumer(mq);p1.start();p2.start();c1.start();System.out.println("main end");}
}class Consumer extends Thread{MyQueue mq;public Consumer(MyQueue mq) {this.mq = mq;}public void run() {for(int i = 0; i < 10; i++) {System.out.println(mq.poll() + "被移除");}}
}class Produce1 extends Thread{MyQueue mq;public Produce1(MyQueue mq) {this.mq = mq;}public void run() {System.out.println("Produce1启动");for (char ch = 'A'; ch <= 'E'; ch++) {mq.offer(ch);}System.out.println("Produce1结束");}
}
class Produce2 extends Thread{MyQueue mq;public Produce2(MyQueue mq) {this.mq = mq;}public void run() {System.out.println("Produce2启动");for (char ch = 'F'; ch <= 'J'; ch++) {mq.offer(ch);}System.out.println("Produce2结束");}
}
//我的队列
class MyQueue{private List values = new ArrayList();private int max = 4;//存入队列public synchronized void offer(Object o) {if(values.size() == max) {//进来线程,停下try {this.wait();//唤醒} catch (InterruptedException e) {e.printStackTrace();}}this.notifyAll();System.out.println(Thread.currentThread().getName() + "存入第" + (values.size() + 1) + "个元素");values.add(o);}//从队列取出public synchronized Object poll() {if(values.size() == 0) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.notifyAll();//唤醒因mq对象而进入无限期等待的线程对象(一个)return values.remove(0);}public void show() {for (Object obj : values) {System.out.println(obj);}}
}
龟兔赛跑问题
/*** //需要又有个赛道:Race-----乌龟/兔子---共用赛道*/
public class Race implements Runnable {//定义变量 :胜利者public static String winner ; //默认值:null@Overridepublic void run() {//定义一个for循环;模拟 1-100之间:步数for(int x = 1 ; x<=100; x ++){//需要让兔子模拟 它睡觉//如果线程对象所在的名称是兔子 并且 是 每10步if(Thread.currentThread().getName().equals("兔子")&& (x % 10==0)){//睡眠50毫秒try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束boolean flag = gameOver(x) ;//参数步数if(flag){//truebreak ;}System.out.println(Thread.currentThread().getName()+"跑了---->"+x+"步"); //比赛结束之后打印出来每一个线程跑了多少不}}//定义一个比赛是否结束的方法private boolean gameOver(int steps) {//步数//情况1:如果当前已经存在胜利者---比赛结束if(winner!=null){return true ;}{//情况2//如果steps>=100 :说明已经跑完了if(steps>=100){// //记录下一胜利者 :winner = Thread.currentThread().getName() ;//胜利者System.out.println("winner is--->"+winner);return true ;}}return false ;}//用户线程public static void main(String[] args) {//将赛道被共用Race race = new Race() ;//创建两条线程Thread t1 = new Thread(race,"兔子") ;Thread t2 = new Thread(race,"乌龟") ;//启动线程t1.start();t2.start();}
}
线程池
线程池概念
现有问题:线程是宝贵的资源,单个线程约占1MB空间,过多分配易造成内存溢出。频繁的创建及销毁线程会增加虚拟机的回收频率,资源开销,造成程序性能下降
线程池:线程容器,可设定线程分配的数量上限将预先创建的线程对象存入池中,并重用线程池中的线程对象避免频繁的创建和销毁
线程池原理
将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程
获取线程池
常用的线程池接口和类(所在包java.util.concurrent);Executor: 线程池顶级接口ExecutorService: 线程池接口,可通过submit(Runnable task) 提交任务代码Executors工厂类: 通过此类可以获得一个线程池通过newFixedThreadPool(int nThreads) 获取固定数量的线程池,参数: 指定线程池中线程的数量通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,没有上限void shutdown():关闭线程池中以前提交的异步任务!
public class TestThreadPool {public static void main(String[] args) {//线程池(引用) ---》Executors工具类(工厂类)ExecutorService es = Executors.newFixedThreadPool(3);Runnable task = new MyTask();es.submit(task);es.submit(task);es.submit(task);es.submit(task);}}class MyTask implements Runnable{@Overridepublic void run() {for(int i = 1; i <= 1000; i++) {System.out.println(Thread.currentThread().getName() + " MyTask:" + i);}}}
Callable接口
Callable接口:public interface Callable<V>{public V call() throws Exception;}JDK5.0加入,与Runnable接口类似,实现之后代表一个线程任务Callable具有泛型返回值,可以声明异常
Future接口
Future接口:概念:异步接受ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)
public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {//使用工厂类创建线程池对象ExecutorService pool = Executors.newFixedThreadPool(2);//提交异步任务/* pool.submit(new MyRunnable()) ;pool.submit(new MyRunnable()) ;*/Future<Integer> f1 = pool.submit(new MyCallable(100));Future<Integer> f2 = pool.submit(new MyCallable(200));//获取结果Integer result1 = f1.get();Integer result2 = f2.get();System.out.println(result1);System.out.println(result2);//关闭线程池pool.shutdown();}
}
import java.util.concurrent.Callable;//两个线程分别进行计算结果:求和
public class MyCallable implements Callable<Integer> {//声明变量private int number ;public MyCallable(int number){this.number = number ;}@Overridepublic Integer call() throws Exception { //call方法的返回值和Callbe<类型>保持一致int sum = 0 ; //结果变量for(int x = 1 ; x<= number ; x ++){sum += x ;}return sum;}
}
线程安全的集合
CopyOnWriteArrayList:线程安全的ArrayList,加强版读写分离写有锁,读无锁,读写之间不阻塞,优于读写锁写入时,先copy一个容器副本,再添加新元素,最后替换引用使用方式与ArrayList无异CopyOnWriteArraySet:线程安全的Set,底层使用CopyOnWriteArrayList实现唯一不同在于,使用addIfAbsent()添加元素,会遍历数组如存在元素,则不添加(扔掉副本)ConcurrentHashMap:初始容量默认为16段(segment) , 使用分段锁设计不对整个Map加锁,而是为每个segment加锁当多个对象存入同一个segment时,才需要互斥最理想的状态为16个对象分别存入16个segment,并行数量为16使用方式与HashMap无异
public class TestCopyOnWriteArrayList {public static void main(String[] args) {List<String> list = new CopyOnWriteArrayList<String>();//接口引用指向实现类对象,更容易更换实现list.add("A");Set<String> set = new CopyOnWriteArraySet<String>();set.add("B");Map<String,String> map = new ConcurrentHashMap<String,String>();map.put("","");}}
Collections中的工具方法
Collections中的工具方法:Collections工具类中提供了多个可以获得线程安全的方法public static <T> Collection<T> synchronizedCollection(Collection<T> c)public static <T> List<T> synchronizedList(List<T> list)public static <T> Set<T> synchronizedSet(Set<T> s)public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)public static <K,V> SortedMap(K,V) synchronizedSortedMap(SortedMap<K,V> m) JDK1.2提供,接口统一,维护性高,但性能没有提升,均以synchronized实现
public class TestCollectionFonSyn {public static void main(String[] args) {List<String> list = new ArrayList<String>();List<String> safeList = Collections.synchronizedList(list);safeList.add("A");safeList.add("A");safeList.add("A");safeList.add("A");}}class SafeCollection<E>{private Collection c = null;final Object o = new Object();public SafeCollection(Collection c) {this.c = c;}public void add(E e) {synchronized(o) {c.add(e);}}}
Queue接口(队列)
Queue接口(队列): Collection的子接口,表示队列FIFO(First In First Out)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBY3x7Xk-1615108342170)(Collection体系集合.png)]
常用方法
抛出异常:boolean add(E e) //顺序添加一个元素(到达上限后再添加,则会抛出异常)E remove() // 获得第一个元素并移除(如果队列没有元素时,则会抛出异常)E element() // 获得第一个元素但不移除(如果队列没有元素,则会抛出异常)
返回特殊值:推荐boolean offer(E e) // 顺序添加一个元素(到达上限后再添加,则会返回false)E poll() // 获得第一个元素并移除(如果队列没有元素,则会返回null)E peek() // 获得第一个元素但不移除(如果队列没有元素时,则会返回null)
ConcurrentLinkedQueue
ConcurrentLinkedQueue:线程安全,可高效读写的队列,高并发下性能最好的队列无锁,CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)V:要更新的变量;E: 预期值;N:新值只有当V = E时,V = N; 否则表示已被更新过,则取消当前操作
public class TestQueue {public static void main(String[] args) {// Queue q = new LinkedList();Queue q = new ConcurrentLinkedQueue();q.offer("A");q.offer("B");q.offer("C");System.out.println(q.poll());System.out.println(q.poll());System.out.println(q.poll());}}
BlockingQueue(阻塞队列)
BlockingQueue接口(阻塞队列)Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法方法:void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待E take() // 获取并移除此队列头部元素,如果没有可用元素,则等待可用于解决生产者和消费者的问题
ArrayBlockingQueue
ArrayBlockingQueue:数组结构实现,有界队列。(手工固定上限)BlockingQueue<String> abq = new ArrayBlockingQueue<String>(10)
LinkedBlockingQueue
LinkedBlockingQueue:链表结构实现,无界队列,(默认上限:Integer.MAX_VALUE)BlockingQueue<String> lbq = new LinkedBlockingQueue<String>()
Timer定时器
java.util.Timer:定时器 (定时工具)
可安排任务执行一次,或者定期重复执行
构造方法
public Timer():创建一个定时器
成员方法
public void cancel()终止此计时器,丢弃所有当前已安排的任务public void schedule(TimerTask task,Date time):在指定的日期时间时完成定时任务参数1:为定时任务(定时业务...)参数2:规定的日期时间(String:"2021-2-25 18:00:00")public void schedule(TimerTask task,long delay,long period):固定延迟的时间间隔来完成这个任务(重复执行的)public void schedule(TimerTask task,long delay):在指定延迟时间后执行这个任务
TimerTask定时任务
定时任务:TimerTask :抽象类 ------> implements Runnablepublic abstract void run() :指定计时器任务操作
public class ThreadDemo2 {public static void main(String[] args) {//创建一个定时器Timer timer = new Timer() ;//启用定时任务//public void schedule(TimerTask task,long delay):在指定延迟时间后执行这个任务// timer.schedule(new MyTask(timer),3000); //3秒后这个任务//public void schedule(TimerTask task,long delay,long period):固定延迟的时间间隔来完成这个任务(重复执行的)timer.schedule(new MyTask(),2000,3000); //2秒后执行这个定时任务,然后每经过3秒重复执行定时任务}
}
//自定一个类 继承TimerTask
class MyTask extends TimerTask{private Timer t ;public MyTask(){}public MyTask(Timer t){this.t = t ;}@Overridepublic void run() {//for(int x = 0 ; x< 5 ;x ++){// System.out.println("bom...");
// t.cancel(); //终止了//}System.out.println("bom");}
}
题目
需求:在 "2021-2-25 18:00:00"---->删除 D盘下:JavaEE_2011day_2_25 中所有后缀为.java文件描述这个文件----->java.io.File类public void schedule(TimerTask task,Date time)import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class Test05 {public static void main(String[] args) throws ParseException {Timer timer = new Timer();String str = "2021-2-26 08:36:00";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse(str);timer.schedule(new MyTask(),date);}
}class MyTask extends TimerTask {private Timer t;public MyTask(){}public MyTask(Timer t){this.t = t;}@Overridepublic void run() {File file = new File("K://source//");File[] files = file.listFiles();if(files != null){for(File f : files){if(f.isFile()){if(f.getName().endsWith(".java")){System.out.println(f.delete());}}}}}
}
设计模式
面向对象设计原则
面向对象思想设计原则
在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则
单一职责原则
其实就是开发人员经常说的”高内聚,低耦合”
也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则
开闭原则
核心思想是:一个对象对扩展开放,对修改关闭。
其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。 也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到 这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则 是可以改变和扩展的。
里氏替换原则
核心思想:在任何父类出现的地方都可以用它的子类来替代。
其实就是说:同一个继承体系中的对象应该有共同的行为特征。
依赖注入原则
核心思想:要依赖于抽象,不要依赖于具体实现。
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他 类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
接口分离原则
核心思想:不应该强迫程序依赖它们不需要使用的方法。
其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一 个接口中。
迪米特原则
核心思想:一个对象应当对其他对象尽可能少的了解
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部 工作原理,它可以使各个模块耦合度降到最低,促进软件的复用
常见见设计模式
设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。好处:使用设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式不是一种方法和技术,而是一种思想 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设 计的程序可复用 学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成
设计模式的分类
创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)\行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备
忘录模式、迭代器模式、解释器模式。(10个)
简单工厂模式
简单工厂----(静态工厂方法模式)提供一个工厂类:提供静态的功能 (使用多态:提高扩展性):负责创建一些类的实例!优点:不需要让具体类创建对象了
弊端:一旦有新的类型增加,需要修改工厂类代码,代码量增加了...
public class PatternDemo {public static void main(String[] args) {//没有使用设计模式之前://对象的创建Dog d = new Dog() ;d.eat();d.sleep();Cat c = new Cat() ;c.eat();c.sleep();System.out.println("--------------------------");//简单工厂//需要通过一个工厂类:创建这些具体动物实例!/*Cat cat = AnimalFactory.createCat();cat.eat();cat.sleep();Dog dog = AnimalFactory.createDog();dog.eat();dog.sleep();Pig pig = AnimalFactory.createPig();pig.eat();pig.sleep();*/Animal animal = AnimalFactory.createAnimal("dog"); //new Dog() ;animal.eat();animal.sleep();animal = AnimalFactory.createAnimal("cat"); //new Cat() ;animal.eat();animal.sleep();animal = AnimalFactory.createAnimal("pig"); //new Pig() ;animal.eat();animal.sleep();animal = AnimalFactory.createAnimal("monkey") ;if(animal!=null){animal.sleep();animal.eat();}else{System.out.println("工厂类中没有提供该动物实例的创建...");}}
}
/*** 工程类---创建具体动物实例*/
public class AnimalFactory {//里面一个功能静态的功能//创建猫类/* public static Cat createCat(){return new Cat() ;}//创建狗类public static Dog createDog(){return new Dog() ;}//创建猪类对象public static Pig createPig(){return new Pig() ;}*///返回值使用父类型public static Animal createAnimal(String type){//参数:为当前类型if("dog".equals(type)){return new Dog() ; //Animal 对象名 = new Dog() ;//多态}else if("cat".equals(type)){return new Cat() ;}else if("pig".equals(type)){return new Pig() ;}else{return null ;}}
}
public class Animal {public void eat(){System.out.println("动物需要吃饭...");}public void sleep(){System.out.println("动物都需要休息...");}
}
//具体的狗类
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃骨头...");}@Overridepublic void sleep() {System.out.println("狗躺着睡觉...");}
}
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼...");}@Overridepublic void sleep() {System.out.println("猫趴着睡觉...");}
}
public class Pig extends Animal {@Overridepublic void eat() {System.out.println("猪吃白菜...");}@Overridepublic void sleep() {System.out.println("猪卧着睡觉...");}
}
工厂方法模式
工厂方法模式1)有一个抽象类:Animal----具体的子类:(Dog/Cat)2)工厂接口----->创建具体类的实例 (抽象功能)3)每一个具体的类----都对应一个该类的工厂(DogFactory,CatFactory)优点:每一个具体的类都使用具体的工厂类创建该类实例弊端: 代码量增加!!
public class PatternDemo {public static void main(String[] args) {Factory factory = new CatFactory() ; //接口多态Animal animal = factory.createAnimal(); //new Cat() ;animal.eat();animal.sleep();System.out.println("-----------------------");factory = new DogFactory() ;animal = factory.createAnimal() ;animal.eat();animal.sleep();}
}
public abstract class Animal { //动物类public abstract void eat() ;public abstract void sleep() ;
}
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃骨头...");}@Overridepublic void sleep() {System.out.println("狗趴着睡觉...");}
}
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");}@Overridepublic void sleep() {System.out.println("猫侧着睡觉");}
}
//工厂接口:创建具体的动物实例
public interface Factory {//提供扩展性---使用多态public Animal createAnimal() ;
}
//狗的工厂类
public class DogFactory implements Factory {@Overridepublic Animal createAnimal() {return new Dog() ;}
}
//猫的工厂类
public class CatFactory implements Factory {@Overridepublic Animal createAnimal() { //抽象类多态return new Cat();}
}
单例设计模式
概述
单例模式就是要确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。优点:在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系 统的性能。
缺点:没有抽象层,因此扩展很难。 职责过重,在一定程序上违背了单一职责
分类
饿汉式
饿汉式:(不会出现问题的单例!)特点:在类加载的时候就创建该类实例!1)在某个类中,成员变量位置:创建该类的静态实例2)构造方法私有化3)需要提供静态的功能,返回值是它本身 ---创建静态实例例子:Runntime类就是单例,是饿汉式
public class Student {//为了保证数据的安全性:加入私有修饰private static Student s = new Student() ;private Student(){}//构造方法私有化//提供对外的公共访问---静态的public static Student getStudent(){return s ;}}
public class SinglePattern {public static void main(String[] args) throws IOException {//使用Student类//外界:依然可以通过无参构造方法创建对象/*Student s1 = new Student() ;Student s2 = new Student() ;System.out.println(s1==s2);*/Student s1 = Student.getStudent();Student s2 = Student.getStudent();System.out.println(s1==s2);System.out.println(s1);System.out.println(s2);System.out.println(s1.getClass() == s2.getClass());//getClass()---->获取正在运行的Java类对象:Class字节码文件对象//Class ----class com.qf.pattern_06.StudentSystem.out.println("---------------------------");Runtime runtime = Runtime.getRuntime();runtime.exec("calc") ;//里面执行一些指令runtime.exec("shutdown -s -t 3600") ;//里面执行一些指令runtime.exec("shutdown -a") ;//取消关机int count = runtime.availableProcessors();System.out.println(count);//cpu核数}
}
懒汉式
懒汉式1)某个类中,构造方法私有化---目的:外界不能够直接通过构造方法创建对象2)成员变量位置:声明一个静态变量: 该类型3)提供对外的公共访问方法: 静态需要判断:如果当前变量为null,这个时候,创建一个对象!懒汉式:可能出现问题的单例模式!延迟加载懒加载都可能造成多线程安全问题将当前这个静态的公共的方法---加入同步代码块---解决线程安全问题!
public class Teacher {private static Teacher t ; //类型:Teacher//构造方法私有化private Teacher(){}//提供静态的对外的公共访方法//t1,t2,t3 ----线程/* public static Teacher getTeacher(){//如果当前对象存在,一直使用;如果为null,就创建一个对象!//判断//每一个线程对象---都在访问资源(访问冲突)synchronized (Teacher.class){if(t == null){t = new Teacher() ;}return t ;}}*///将synchronized定义在方法声明上public synchronized static Teacher getTeacher(){ //锁对象:Teacher.class//如果当前对象存在,一直使用;如果为null,就创建一个对象!//判断//每一个线程对象---都在访问资源(访问冲突)if(t == null){t = new Teacher() ;}return t ;}
}
IO框架
File类
概念
java.io.File:文件和目录(文件夹)路径名的抽象表示形式。
构造方法
构造方法public File(String pathname):直接传递字符串路径 (推荐....)public File(File parent,String child):参数1:File对象:某个目录(文件夹)File对象参数2:当前指定的具体路径public File(String parent,String child):分别指定父目录的路径以及子文件的路径
功能
创建功能
创建文件:public boolean createNewFile() throws IOException 如果不存在,创建空文件,返回true;否则,false必须存在目录,目录都不存在(系统找不到指定的路径。)
创建文件夹(目录)public boolean mkdir():创建目录(文件夹)public boolean mkdirs():创建此抽象路径名指定的目录,包括所有必需但不存在的父目录时,创建!如果操作File所示的路径:没有带盘符----->文件/目录创建到哪里了?
将文件或者文件夹创建在当前项目路径下!
删除功能
删除功能:public boolean delete():可以删除文件或者文件夹(目录),如果要删除目录,前提必须是空目录!
重命名功能
重命名功能:public boolean renameTo(File dest)重新命名此抽象路径名表示的文件将D盘下这个文件进行重命名情况1:重名的时候:源文件/目标文件都是相同地址 :d盘下----只是重命名情况2:源文件/目标文件不再同一个路径下: 剪切+重命名
判断功能
判断功能:public boolean isAbsolute() :是否为绝对路径public boolean isDirectory():File所表示的路径是否指定的是一个目录public boolean isFile():是否是文件public boolean isHidden():是否是一个隐藏文件public boolean canRead():是否可读public boolean canWrite():是否可写public boolean exists():是否存在
获取功能
获取功能public String getAbsolutePath():获取绝对路径public String getPath():获取路径long lastModified():获取文件最后一个被修改的时间public String getName():获取文件/目录的名称public long length():获取文件的长度(单位是字节...)public String[] list() :获取当前路径下所表示的文件或者文件夹的字符串数组public File[] listFiles():获取某个路径的下所表示的文件或者文件夹的File数组
高级获取功能
高级获取功能通过文件名称过滤器:获取某个路径下所有 的文件以及文件夹的String[]/File[]数组public File[] listFiles(FilenameFilter filter) :public String[] list(FilenameFilter filter) :
递归
递归:方法调用方法本身的一种现象,并非方法嵌套方法!Math.max(Math.max(10,20),35) ; 这个方法嵌套
递归的条件
递归的条件:1)必须定义一个方法:通过方法调用方法本身的一种现象2)必须有出口条件(结束条件),否则死递归3)存在一定的规律...注意:构造方法:没有递归的!
阶乘
public class DiGuiDemo {/* public DiGuiDemo(){ //构造方法
// DiGuiDemo() ;}*/public static void main(String[] args) {//循环//阶乘思想:最终结果变量int jc = 1 ;for(int x= 1; x <=5 ; x ++){jc *= x ;}System.out.println("5的阶乘是:"+jc);System.out.println("---------------------------------");//递归的思想System.out.println("5的阶乘是:"+getNum(5));}//定义一个方法private static int getNum(int n) {//5//4 //3//结束条件if(n==1){return 1 ;}else{return n*getNum(n-1) ;}}
}
不死神兔
/*** 需求:[不死神兔],有一对兔子,三个月后每一个月产生一对兔子,小兔子长到第三个月又产生一对兔子* 假设兔子都不死,第20个月,总共有多少对兔子!*** 分析:* 第一个月:1* 第二个月:1* 第三个月:2* 第四个月:3* 第五个月:5* 第六个月:8* .....** 规律:* 已知两个数据:第一个月/第二个月:1* 从第三个月开始:每个月兔子的对数:等于前两个月之和!** 结束条件:第一个月和第二个都是1** 解决方案:* 1)数组方式* 2)递归方式*/
public class DiGuiTest {public static void main(String[] args) {//方式1:定义一个数组:动态初始化int[] arr = new int[20] ;//角标:0开始,, 长度20//第一个月和第二个月都是1arr[0] = 1 ;arr[1] = 1 ;// 从第三个月开始:每个月兔子的对数:等于前两个月之和!for(int x = 2 ;x < arr.length ; x++){arr[x] = arr[x-1] + arr[x-2] ;}System.out.println("第二十个月兔子的对数有:"+arr[19]);//6765System.out.println("----------------------------------------------");//定义一个方法:getRabbitNum(20): 参数代表:第几个月System.out.println("第二十个月兔子的对数有:"+getRabbitNum(20));}//n:表示的月份private static int getRabbitNum(int n) {//结束条件:第一个月/第二个月:1if(n==1 || n== 2){return 1 ;}else{//从第三个月开始:中间的月份:
// 每个月兔子的对数:等于前两个月之和!//3 (3-1) 3-2return getRabbitNum(n-1) + getRabbitNum(n-2) ;//6765}}
}
递归删除指定的目录
/*** 需求:递归删除指定的目录** 删除:当前 项目下的demo文件夹* 分析:** 1)封装文件夹(目录):File对象* 2)定义一个删除文件夹方法* deleteSrc(file对象)** 3)* 获取File对象所表示路径下的所有文件夹以及文件的File数组* 4)判断数组不为空,遍历* 获取到每一个File对象* 判断:如果这个file对象代表的是文件夹isDirectory()* 继续回到2)* 如果不是文件夹,* 直接删除文件,(查看删除了谁,获取文件名称)** 5)直接将demo文件删除掉*****/
public class DiGuiTest2 {public static void main(String[] args) {//创建File对象:表示抽象路径File srcFile = new File("demo") ;//定义一个删除文件夹的方法deleteSrc(srcFile) ;}//递归删除文件夹的方法public static void deleteSrc(File srcFile) {//获取这个srcFile下面的所有文件夹以及文件的file数组File[] fileArray = srcFile.listFiles();//防止空指针if(fileArray!=null){//遍历File数组for(File file:fileArray){//判断如果file对象是文件夹if(file.isDirectory()){//继续调用 deleteSrc方法来删除deleteSrc(file);}else{//是文件System.out.println(file.getName()+"---"+file.delete());}}//删除demo文件System.out.println(srcFile.getName()+"---"+srcFile.delete());}}
}
题目
/*** 需求:* 获取指定盘符下的文件并且该文件是以.* jpg结尾的文件** 分析:* 1)使用File对象描述一个盘符:D盘* 2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组* 3)如果File[]不为空,然后遍历----获取到每一个File对象* 3.1) 如果当前File对象所表示的是一个文件:boolean isFile()* 3.2)如果是文件,要判断当前文件是以.jpg结尾的文件* String类:endsWith(String xx)* 获取文件名称...***/public class FileTest {public static void main(String[] args) {//1)使用File对象描述一个盘符:D盘File srcFile = new File("D://") ;//2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组File[] fileArray = srcFile.listFiles();//非空判断if(fileArray!=null){//遍历---获取到每一个File对象for(File file: fileArray){// 3.1) 如果当前File对象所表示的是一个文件:boolean isFile()if(file.isFile()){//是文件//如果是文件,要判断当前文件是以.jpg结尾的文件if(file.getName().endsWith(".jpg")){//获取文件System.out.println(file.getName());}}}}}
}
/*** 需求:* 获取指定盘符下的文件并且该文件是以.* jpg结尾的文件** 分析:* 1)使用File对象描述一个盘符:D盘* 2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组* 3)如果File[]不为空,然后遍历----获取到每一个File对象* 3.1) 如果当前File对象所表示的是一个文件:boolean isFile()* 3.2)如果是文件,要判断当前文件是以.jpg结尾的文件* String类:endsWith(String xx)* 获取文件名称...*** 上面这种可以,麻烦---- 获取到了所有的文件以及文件夹的File数组---->加入一系列判断!** 在File对象(D盘符)获取的时候整个盘符下是文件以及文件夹File数组,就已经能够拿到每一个file对象?* 文件名称过滤器:FilenameFilter** 通过文件名称过滤器:获取某个路径下所有 的文件以及文件夹的String[]/File[]数组* public File[] listFiles(FilenameFilter filter) :* 形式参数:是一个接口类型:文件名称过滤器 -----匿名内部类/自定义一个子实现类* boolean accept(File dir,String name)测试指定文件是否应该包含在某一文件列表中。* 参数1:dir:指定的文件所在的目录* 参数2:当前文件的名称** 返回值:true/false取决于 当前是否能够将当前dir所在的目录中指定的文件名称添加在文件列表中* true:添加到文件列表中* false:不会添加到文件列表中*/
public class FileTest2 {public static void main(String[] args) {//表示D盘File srcFile = new File("D://") ;//直接获取到文件列表// public File[] listFiles(FilenameFilter filter) :// public String[] listFiles(FilenameFilter filter) :String[] strArray = srcFile.list(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {// return false;//加入自己的逻辑//返回值为true:将文件名称所在的目录添加到列表中//dir:目录 name:文件名称File file = new File(dir,name) ;// System.out.println(file);// return true ;//需要的是文件/以及以.jpg结尾的文件/*boolean flag1 = file.isFile() ;//判断是文件boolean flag2 = file.getName().endsWith(".jpg");return flag1 && flag2 ;*/return file.isFile() && file.getName().endsWith(".jpg") ;//}});for(String s : strArray){System.out.println(s);}}
}
IO流
流的概念
概念:内存与存储设备之间传输数据的通道
流的用途
IO流:-----> 底层创建系统资源,在不同的设备之间进行数据传输的!
I: 输入---->磁盘上有一个文件,让通过流的方式将文件读出来!
O:输出 ---->在内存中—通过输出流,在某个磁盘下输出文件并且写入内容
流的分类
IO流的分为:按流的方向输入流:将存储设备中的内容读取到内存中输出流:将内存中的内容写入到存储设备中按流的类型:字节流:以字节为单位,可以读取所有数据字符流:以字符为单位,只能读写文本数据
字节流
字节流的抽象基类:
InputStream ,OutputStream。xxxxInputStream
XXXXOutputStream
都是子类
字节输入流
InputStream : 字节输入流 public int read(){}public int read(byte[] b){}public int read(byte[] b, int off, int len){}
字节文件输入流
FileInputStream读取 fos3.txt 文件使用步骤:1)创建具体的子类对象:FileInputStreampublic FileInputStream(String name)2)读取数据:一次读取一个字节/一次读取一个字节数组public abstract int read()throws IOExceptionpublic abstract int read(byte[] bytes)throws IOException3)释放资源
public class FileInputStreamDemo {public static void main(String[] args) throws IOException {//创建文件输入流对象FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java") ;//单独将带中文的文件---输出在控制台上,由于强制类型转换---一个中文(UTF-8)对应三个字节,跟前面的英文字符对应的字节拼接不上,所以造成乱码//使用循环实现----不知道循环多少次,使用while//结束条件:当前字节数为-1,流已经到末尾了//一次读取一个字节//将赋值,判断,以及获取写在一块int by = 0 ; //字节数开始为0,没有开始读while((by=fis.read())!=-1){//将获取的都字节数---转换字符System.out.print((char)by);}//释放资源fis.close();}
}public class FileInputStreamDemo2 {public static void main(String[] args) throws IOException {//创建一个文件输入流对象FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java") ;// public abstract int read(byte[] bytes)throws IOException :一次读取一个字节数组//重复性代码:使用循环实现:while循环//结束条件:如果流已经读取到末尾了,返回-1//一次读取一个字节数组:模板代码//一般情况:字节数组的长度:1024或者是1024的整数倍//在使用new String(bytes)获取实际的内容,带上len的使用:实际的内容//String(bytes[] bytes,0,len):每一次读取的时候,都是从角标0开始,读取实际长度!//赋值,判断,获取都写在while循环中byte[] bytes = new byte[1024] ; //构造字节缓冲区int len = 0 ;//开始没有读取while((len=fis.read(bytes))!=-1){System.out.println(new String(bytes,0,len));}//资源释放fis.close();}
}
字节缓冲输入流
字节缓冲输入流对象: (高效的字节输入流)
BufferedInputStream(InputStream in) :默认大小的缓冲区(创建字节缓冲输入流)
public int read()
public int read(byte[] bytes)
public class BufferedInputStreamDemo {public static void main(String[] args) throws IOException {//创建字节缓冲输入流对象BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt")) ;//读取数据//一次读取一个字节/* int by = 0 ;while((by=bis.read())!=-1){System.out.print((char)by);}*///一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = 0 ;while((len=bis.read(bytes))!=-1){System.out.println(new String(bytes,0,len));}// 资源关闭bis.close();}
}
字节输出流
OutputStream: 字节输出流 public void write(int n){}public void write(byte[] b){}public void write(byte[] b, int off, int len){}
字节文件输出流
FileOutputStream:输出一个文本文件,并且写内容使用步骤:1)创建输出流对象public FileOutputStream(String name)throws FileNotFoundException2)写入内容写入的字节....3)关闭资源
写入数据的功能
public void write(int b) throws IOException :写入一个字节public void write(byte[] b) throws IOException:写入一个字节数组public void write(byte[] b,int off,int len):写入当前字节数组的一部分
构造方法
public FileOutputStream(File file,boolean append) throws FileNotFoundException参数1:File对象所表示的文件参数2:是否进行末尾追加(true,文件末尾处追加) 在之前操作记录之上,继续追加!如果输出文件写入内容需要进行换行操作:
换行符号windows操作系统:"\r\n"Linux操作系统中:"\n"Mac系统:"\r"
public class FileOutputStreamDemo3 {public static void main(String[] args) throws IOException {//File表示文件File file = new File("fos3.txt") ;//创建文件输出流对象//public FileOutputStream(File file,boolean append) throws FileNotFoundExceptionFileOutputStream fos = new FileOutputStream(file,true) ;//写入内容for(int x = 0 ; x < 10 ; x ++){fos.write(("hello"+x).getBytes());//写入这个换行符号fos.write("\r\n".getBytes());}//释放资源fos.close();}
}
异常处理
使用文件输出流的时候,加入异常处理:try...catch...finally实际开发中:IO流/JDBC ---->加入异常处理,try...catch...finally:统一处理
public class FileOutputStreamDemo4 {public static void main(String[] args) {//方式1: 跟流对象相关的一一处理// method1() ;//方式2:统一处理method2();}public static void method2() {FileOutputStream fos = null ;try {fos = new FileOutputStream("fos4.txt") ;//写入内容fos.write("hello,FileOutputStream".getBytes());} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {//释放资源if(fos!=null){try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}private static void method1() {//创建文件输出流对象FileOutputStream fos = null ;try{fos = new FileOutputStream("fos4.txt") ;}catch (FileNotFoundException e){e.printStackTrace();}//写入内容try {fos.write("hello,FileOutputStream".getBytes());} catch (IOException e) {e.printStackTrace();}//释放资源try {fos.close();} catch (IOException e) {e.printStackTrace();}}
}
字节缓冲输出流
BufferedOutputStream:字节缓冲输出流(高效的字节流):是OutputStream的子类
作用:内部提供一个(默认大小)缓冲区
构造方法
public BufferedOutputStream(OutputStream out):创建一个默认大小的字节缓冲输出流对象,形式参数:抽象类---实际参数--需要该抽象类的子类对象BufferedInputStream/OutputStream:只是提供一个缓冲区,针对文件读/写的操作,还是需要使用基本字节流:InputStream/OutputStream源码
/*public BufferedOutputStream(OutputStream out) {this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {super(out);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];
}byte[] buf = new byte[8192] ;长度*/
编码表
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就
将各个国家的文字用数字来表示,并一一对应,形成一张表。ASCII:美国标准信息交换码。用一个字节的7位可以表示。ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。GB2312:中国的中文编码表。GBK:中国的中文编码表升级,融合了更多的中文文字符号。GB18030:GBK的取代版本BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicodeUTF-8:最多用三个字节来表示一个字符。UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:它将Unicode编码为00000000-0000007F的字符,用单个字节来表示,它将Unicode编码为00000080-000007FF的字符用两个字节表示,它将Unicode编码为00000800-0000FFFF的字符用3字节表示
public class TestEncoding {public static void main(String[] args) throws UnsupportedEncodingException {String s1 = "你好世界123abc喆";byte[] bs = s1.getBytes("GBK");//GBK文本"编码"为二进制String s2 = new String(bs,"BIG5");//二进制"解码"为GBK文本System.out.println(s2);byte[] bs2 = s2.getBytes("BIG5");String s3 = new String(bs2,"GBK");System.out.println(s3);}}
字符流
桥转换流
字符转换输出流
字符输出流的使用OutputStreamWriter 是字符流通向字节流的桥梁
构造方法
构造方法OutputStreamWriter(OutputStream out)使用平台默认的编码集(UTF-8)--->对写入的内容进行编码 (字符转换输出流)OutputStreamWriter(OutputStream out,String charsetName):执行编码操作
成员方法
void write(char[] cbuf)写入字符数组。
abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分。void write(int c)写入单个字符。void write(String str)写入字符串。void write(String str, int off, int len)写入字符串的某一部分。
public class OutputStreamWriterDemo {public static void main(String[] args) throws IOException {//创建字符转换输出流对象://字符流中指向字节流OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt")) ;//UTF-8格式编码//写入数据
// char[] chs = {'a','b','c','d','e'} ;//void write(char[] cbuf)
// osw.write(chs);//abstract void write(char[] cbuf, int off, int len)
// osw.write(chs,1,2);//void write(int c)
// osw.write('a');osw.write("我爱中国");//刷新流osw.flush();//释放资源osw.close();}
}
字符转换输入流
字符转换输入流:InputStreamReader是字节流通向字符流的桥梁: (解码:将流中的内容使用指定的字符集解码成字符!)
构造方法
public InputStreamReader(InputStream in):平台默认字符集
public InputStreamReader(InputStream in,String charsetName):指定的字符集
成员方法
int read()读取单个字符。
int read(char[] cbuf)将字符读入数组。
abstract int read(char[] cbuf, int off, int len)将部分字符读入数组
public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//读取当前项目osw.txt//指定字符集:GBK
// InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"GBK") ;InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt")) ;//默认:UTF-8//读取内容//int read(char[] cbuf)//一次读取一个字符数组char[] chs = new char[1024] ;int len = 0 ;//字符数while((len=isr.read(chs))!=-1){System.out.println(new String(chs,0,len));}//关闭资源isr.close();}
}
字符文件流
字符流操作文件两种方式1)使用字符转换流操作:读写操作
2)使用FileReader/FileWriter:读写操作
构造方法
public FileReader(String fileName)public FileWriter(String fileName)
public class CopyTest {public static void main(String[] args) throws IOException {//封装源文件FileReader fr = new FileReader("d://FileOutputStreamDemo.java") ;//封装目的地文件FileWriter fw = new FileWriter("MySelf.java") ;//读写操作//一次读取字符数据char[] chs = new char[1024] ;int len = 0 ;while((len=fr.read(chs))!=-1){//读一个字符数组,写入字符数组fw.write(new String(chs,0,len));//刷新fw.flush();}//释放资源fw.close();fr.close();}
}
字符缓冲流
字符缓冲输出流
字符缓冲输出流:BufferedWriter
针对文本来进行高效的写入(高效的字符流)
只是提供缓冲区:针对文件的操作还需要使用基本流
构造方法
public BufferedWriter(Writer out):创建一个默认大小的字符缓冲输出流对象(默认值足够大了)
成员方法
void newLine()写入一个行分隔符。void write(char[] chs)
void write(char[] cbuf, int off, int len)写入字符数组的某一部分。
void write(int c)写入单个字符。
void write(String str)
void write(String s, int off, int len)
public class BuffferedWriterDemo {public static void main(String[] args) throws IOException {//创建一个字符缓冲输出流对象BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ; //默认大小:defaultBufferSize:8192/* BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("bw.txt"))) ;*///直接写入字符串内容bw.write("Hello");//之前的换行:通过系统写入"\r\n"// public void newLine()throws IOExceptionbw.newLine();bw.write("JavaEE");bw.newLine();bw.write("World");bw.newLine();//刷新流bw.flush();//释放资源bw.close();}
}
字符缓冲输入流
字符缓冲输入流:BufferedReader
针对文本高效读取(高效的字符输入流)
构造方法
BufferedReader(Reader read):构造一个默认大小的缓冲输入流对象(默认值足够大)
成员方法
public String readLine()throws IOException :一次读取一行内容,返回值如果为null,表示当前读完了
public int read(int ch):读取一个字符
public int read(char[] chs):读取一个字符
public void read(String str/)
public class BufferedReaderDemo {public static void main(String[] args) throws IOException{//创建一个字符缓冲输入流对象
// BufferedReader br = new BufferedReader(new FileReader("bw.txt")) ;BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;//读取:一次读取一个字符/一次读取一个字符数组/* char[] chs = new char[1024] ;int len = 0 ;//实际字符数while((len=br.read(chs))!=-1){System.out.println(new String(chs,0,len));}*///public String readLine()throws IOException :一次读取一行内容,返回值如果为null,表示当前读完了//使用循环,while循环String line = null ;while((line=br.readLine())!=null){System.out.println(line);}//释放资源br.close();}}
/*** 需求:当前项目下的:BufferedWriterDemo.java文件** 复制到D://copy.java文件中** 源文件:* BufferedReader:读取 BufferedWriterDemo.java文件 (推荐)* 目标文件:* BufferedWriter:写入D://copy.java文件*/
public class CopyFile {public static void main(String[] args) throws IOException {//封装源文件/目标文件BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;BufferedWriter bw = new BufferedWriter(new FileWriter("D://copy.java")) ;//读写复制操作:使用字符缓冲流的特有功能String line = null ;while((line=br.readLine())!=null){//读一行,写一行bw.write(line);bw.newLine();bw.flush(); //刷新}//关闭资源bw.close();br.close();}
}
其他流
打印流
字节打印流
字节打印流:PrintStream
System.out---->标准"输出流"public static final PrintStream out ;打印各种数据表示形式(输出字节,如果是字符----转换成字节)
print(xxx);println(xxx);
字符打印流
字符打印流:PrintWriter只能操作目标文件这个流:他可以启用自动刷新功能public PrintWriter(Writer out,boolean autoFlush) :第二个参数为true:启用自动刷新public void println():写入行的分隔符号,终止当前行
public class PinrtStreamDemo {public static void main(String[] args) throws IOException {PrintStream out = System.out;out.println("hello");out.println("-------------------------------------");out.println(true) ;char[] chs = {'a','b','c'} ;out.println(chs) ;out.println("-------------------------------------");//创建一个字符缓冲输入流对象BufferedReader br = new BufferedReader(new FileReader("BuffferedWriterDemo.java")) ;//创建字符打印流 public PrintWriter(Writer out,boolean autoFlush)PrintWriter ps = new PrintWriter(new FileWriter("demo.java"),true) ;//读写复制操作//一次读取一行String line = null ;while((line=br.readLine())!=null){ps.println(line); //给当前ps指向这个demo.java文件中打印内容//不需要刷新:自动刷新}//释放资源ps.close();br.close();}
}
字节输入流的逻辑串联
操作两个文件
构造方法SequenceInputStream(InputStream s1,InputStream s2)操作两个源文件...
/*** SequenceInputStream:字节输入流的逻辑串联** 构造方法* SequenceInputStream(InputStream s1,InputStream s2)* 操作两个源文件...** 举例* 将当前项目下的BuffferedWriterDemo.java+PinrtStreamDemo.java复制到* 当前项目下的:copy.java文件中**/
public class SequenceInputStreamDemo {public static void main(String[] args) throws IOException {//封装源文件SequenceInputStream sis = new SequenceInputStream(new FileInputStream("BuffferedWriterDemo.java"),new FileInputStream("PinrtStreamDemo.java")) ;//封装目标文件BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.java")) ;//读写操作byte[] bytes = new byte[1024] ;int len = 0 ;//字节数while((len=sis.read(bytes))!=-1){bos.write(bytes,0,len);bos.flush(); //字节流中刷新:强制刷新缓冲区中字节数}bos.close();sis.close();}
}
操作两个文件以上
构造方法:public SequenceInputStream(Enumeration<? extends InputStream> e):针对两个以上的文件进行复制
/*** public SequenceInputStream(Enumeration<? extends InputStream> e):* 针对两个以上的文件进行复制** 举例** D://a.txt + c://b.txt + e://c.txt** ----->当前项目demo.txt文件中*/
public class SequenceInputStreamDemo2 {public static void main(String[] args) throws IOException {//BuffferedWriterDemo.java+PinrtStreamDemo.java+SequenceInputStreamDemo.java//封装源文件//创建Vector集合对象Vector<InputStream> v = new Vector<InputStream>() ;InputStream s1 = new FileInputStream("BuffferedWriterDemo.java") ;InputStream s2 = new FileInputStream("PinrtStreamDemo.java") ;InputStream s3 = new FileInputStream("SequenceInputStreamDemo.java") ;//添加元素v.add(s1) ;v.add(s2) ;v.add(s3) ;//Vector------>获取特有的迭代器(Enumeration)Enumeration<InputStream> en = v.elements(); //获取向量的枚举组件-------- iterator() ---->IteratorSequenceInputStream sis = new SequenceInputStream(en) ;//d://demo.java:目标文件BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D://demo.java")) ;//读写复制操作//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = 0 ;while((len=sis.read(bytes))!=-1){bos.write(bytes,0,len);bos.flush();}//释放资源bos.close();sis.close();}
}
序列化和反序列化
序列化
序列化: ObjectOutputStream
将Java对象(任意Java类的对象) 对象 在网络中进行数据传输-----变成一种"流"数据,这个过程称为序列化
反序列化
反序列化 ObjectInputStream
又需要将流数据-----还原成对象
public class ObjectStreamDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化:
// myWrite();//反序列化myRead() ;}public static void myRead() throws IOException, ClassNotFoundException {//ObjectInputStream//构造方法//public ObjectInputStream(InputStream in)throws IOExceptionObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;//public final Object readObject()throws IOException,ClassNotFoundExceptionObject object = ois.readObject();System.out.println(object); //Person //Person{name='高圆圆', age=41}//释放资源ois.close();}public static void myWrite() throws IOException{//对象 在网络中进行数据传输-----变成一种"流"数据
// public ObjectOutputStream(OutputStream out)throws IOExceptionObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;//public final void writeObject(Object obj)// throws IOException 将对象写入到流中Person p = new Person("高圆圆",41) ;oos.writeObject(p); //写入内容:编码集:GBK//java.io.NotSerializableException: com.qf.objectstream_05.Person//释放资源oos.close();}
}import java.io.Serializable;//只能将支持 java.io.Serializable 接口的对象写入流
//要能够启用序列化功能:自定义对象所在的类必须实现:Serializable
//类通过实现 java.io.Serializable 接口以启用其序列化功能/*** 实现了序列化接口(标记接口)的类----在内存中会产生一个序列化的版本Id: SerialVersionUID:唯一标识符* 类----对应类的签名:就是一个唯一的id值** 序列化---反序列化----如果现在手动更改成员信息----如果直接在进行反序列化 :** java.io.InvalidClassException: com.qf.objectstream_05.Person; local class incompatible: stream classdesc serialVersionUID = 3971989068296701088,* local class serialVersionUID = 5645013692103785236** 该类的序列版本号与从流中读取的类描述符的版本号不匹配 :** 序列化的时候:com.qf.objectstream.Person:会产生一个serialVersionUID* 然后反序列化的时候:会读取当前Person在内存中的序列化版本ID(一致),获取Java对象* 如果更改了成员信息,直接进行反序列化,序列化版本Id不一致,出现这个异常!* 产生一个固定的序列化版本Id号(常量)或者针对某个成员加入这个关键字: transient:针对某个类的成员不会参与序列化/反序列化!***/
public class Person implements Serializable {private transient String name ;int age ;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
属性集合类
属性集合类:Properties extends Hashtable<K,V>
Properties 类表示了一个持久的属性集。可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
这个属性集合类:应该具备Map集合的功能
public class PerpertiesDemo {public static void main(String[] args) {//创建属性集合类对象//使用Map接口的功能Properties prop = new Properties() ;//添加元素:直接使用put方法prop.put("1","张三") ;prop.put("2","高圆圆") ;prop.put("3","赵又廷") ;prop.put("4","文章") ;//遍历Map集合的遍历几种方式://通用:keySet()Set<Object> set = prop.keySet();for(Object key :set){Object value = prop.get(key);System.out.println(key+"---"+value);}}
}
构造方法
public Properties():无参构造
特有功能
添加功能
添加元素:public Object setProperty(String key,String value)
获取功能
获取:public Set<String> stringPropertyNames():获取属性列表中所有的键集public String getProperty(String key):通过属性列表中的属性名称-->获取属性值
public class PropertiesDemo2 {public static void main(String[] args) {//创建属性列表对象Properties prop = new Properties() ;System.out.println(prop);//添加元素prop.setProperty("高圆圆","赵又廷") ;prop.setProperty("文章","马伊琍") ;prop.setProperty("孙悟空","紫霞仙子") ;System.out.println(prop);//遍历public Set<String> stringPropertyNames():获取属性列表中所有的键集Set<String> set = prop.stringPropertyNames();for(String key :set){String value = prop.getProperty(key);System.out.println(key+"---"+value);}}
}
加载功能
加载功能:public void load(Reader reader) throws IOException:public void load(InputStream in)throws IOException将某个文件的内容加载到当前属性列表中
保存功能
保存:public void store(Writer writer,String comments)将属性列表中的内容保存到指定的文件中...
public class PropertiesDemo3 {public static void main(String[] args) throws IOException {// myStore() ;myLoad() ;}//将文件中的内容加载属性集合类中private static void myLoad() throws IOException {//创建属性集合列表对象Properties prop = new Properties() ;System.out.println(prop);System.out.println("---------------------");//从文本文件中获取信息加载属性集合类中prop.load(new FileReader("user.txt"));System.out.println(prop);}//将属性列表中的内容:保存到某个文件中private static void myStore() throws IOException {//创建一个属性列表集合对象Properties prop = new Properties() ;//添加内容prop.setProperty("张三","30") ;prop.setProperty("高圆圆","41") ;prop.setProperty("李四","40") ;prop.setProperty("文章","35") ;// public void store(Writer writer,String comments)//参数1:字符输出流//参数2:给当前属性列表中添加描述信息prop.store(new FileWriter("username.txt"),"student's list");}
}
网络编程
计算机网络
网络
网络:由点和线构成,表示诸多对象间的相互联系
计算机网络
计算机网络:为实现资源共享和信息传递,通过通信线路连接起来的若干主机(host)互联网:Internet 点与点相连万维网:WWW - World Wide Web 端与端相连物联网:IoT - Internet of things 物与物相连
网络编程
网络编程:让计算机与计算机之间建立连接,进行通信
网络模型
网络模型
网络模型:OSI:(Open System Interconnection) 开放式系统互联开放系统A 开放系统B应用层协议应用层 <------------> 应用层 第七层:应用层负责文件访问和管理,可靠运输服务,远程操作服务(HTTP,FTP,SMTP)表示层协议表示层 <------------> 表示层 第六层:表示层负责定义转换数据格式及加密,允许选择以二进制或ASCII格式传输会话层协议会话层 <------------> 会话层 第五层:会话层负责使应用建立和维持会话。使通信在失效时继续恢复通信(断点续传)传输层协议传输层 <------------> 传输层 第四层:传输层负责是否选择差错恢复协议,数据流重用,错误顺序重排(TCP,UDP)网络层 网络层 第三层:网络层负责定义了能够标识所有网络节点的逻辑地址(IP地址)数据链路层 数据链路层 第二层:链路层在物理层上,通过规程或协议(差错控制)来控制传输数据的正确性(MAC)物理层 物理层 第一层:物理层为设备之间的数据通信提供传输信号和物理介质(双绞线,光导纤维)
《---------通信介质(物理媒体)----------》
TCP/IP模型
TCP/IP模型:一组用于实现网络互连的通信协议,将协议分为四个层次TCP/IP模型:应用层 第四层:应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型的协议是HTTP,FTP等传输层 第三层:传输层负责传输文本数据,主要协议是TCP协议,UDP协议网络层 第二层:网络层负责分配地址和传送二进制数据,主要协议是IP协议网络接口层 第一层:接口层负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网,ADSL等等
TCP/UDP
TCP
TCP协议:Transmission Control Protocol 传输控制协议是一种面向连接的,可靠的,基于字节流的传输层通信协议,数据大小无限制,建立连接过程需要三次握手,断开连接过程需要四次挥手
UDP
UDP协议:User Datagram Protocol 用户数据报协议是一种无连接的传输协议,提供面向事物的简单不可靠信息传送服务,每个包的大小是64KB
IP
IP协议
IP协议:Internet Protocol Address 互联网协议地址/网际协议地址分配给互联网设备的数字标签(唯一标识)
IP地址
IPV4
IP地址分为两种:IPV4:4字节32位整数,并分为4段8位二进制数,每8位之间用圆点隔开,每8位整数可以转换为一个0~255的十进制整数格式:D.D.D.D 例如:255.255.255.255
IPV4的应用分类
A类:政府机构:1.0.0.1~126.255.255.254
B类:中型企业:128.0.0.1~191.255.255.254
C类:个人用户:192.0.0.1~223.255.255.254
D类:用于组播:224.0.0.1~239.255.255.254
E类:用于实验:240.0.0.1~255.255.255.254回环地址:127.0.0.1 指本机,一般用于测试使用
查看命令:ipconfig
测试IP命令:ping D.D.D.D
IPV6
IPV6:16字节128位整数,并分为8段十六进制数,每16位之间用圆点隔开,每16位整数可以转换为一个0~65535的十进制数格式:X.X.X.X.X.X.X.X 例如:FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF
端口(Port)
端口号
端口号:在通信实体上进行网络通讯的程序的唯一标识
端口分类
端口分类:公认端口:0~1023注册端口:1024~49151动态或私有端口:49152~65535
常用端口
常用端口:MySql: 3306Oracle: 1521Tomcat: 8080SMTP: 25web服务器:80FTP服务器:21
InetAddress类
概念
表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法
方法
public static InetAddress getLocalHost()获得本地主机地址对象
public static InetAddress getByName(String host) 根据主机名称获得Ip地址对象参数:要么是知道机器名称的情况/要么就是ip地址字符串形式
public static InetAddress[] getAllByName(String host)获得所有相关地址对象
public String getHostName():获取IP地址主机名称
public String getHostAddress():获取ip地址(字符串形式)
import java.net.InetAddress;
import java.net.UnknownHostException;public class TestInetAddress {public static void main(String[] args) throws UnknownHostException {//获得本机IP地址对象InetAddress localhost = InetAddress.getLocalHost();//获得IP地址字符串System.out.println(localhost.getHostAddress());//获得IP地址主机名System.out.println(localhost.getHostName());System.out.println(localhost);//获得任意主机的IP地址对象(IP、主机名、域名)InetAddress baidu = InetAddress.getByName("www.baidu.com");
// System.out.println(baidu.getHostAddress());
// System.out.println(baidu.getHostName());//获得任意域名所绑定的所有IP地址对象InetAddress[] addrs = InetAddress.getAllByName("www.baidu.com");for(InetAddress addr : addrs) {System.out.println(addr.getHostAddress());System.out.println(addr.getHostName());}}}
基于UDP的网络编程
UDP发送端的使用步骤
UDP发送端的使用步骤:1)创建发送端的Socket对象DatagramSocket:此类表示用来发送和接收数据报包的套接字。构造方法public DatagramSocket() throws SocketException:将任何端口号绑定当前这个数据包套接字上2)创建数据报包对象:DatagramPacket此类表示数据报包。构造方法public DatagramPacket(byte[] buf, 包数据int length, 包长度InetAddress address, 目标地址int port) 端口号3)发送public void send(DatagramPacket p) throws IOException4)释放资源关闭 public void close()
public class UDP_Send {public static void main(String[] args) throws IOException {// 创建发送端的Socket对象DatagramSocket ds = new DatagramSocket() ;//2)创建数据报包对象:DatagramPacket此类表示数据报包/*** public DatagramPacket(byte[] buf,包数据* int length, 包长度* InetAddress address, 目标地址* int port) 端口号*/byte[] bytes = "hello,udp,我来了".getBytes() ;/*int length = bytes.length ;InetAddress inetAddress = InetAddress.getByName("10.12.156.196");int port = 8888 ;*///DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;DatagramPacket dp = new DatagramPacket(bytes,bytes.length,InetAddress.getByName("10.12.156.196"),8888);//3)发送ds.send(dp);//4)释放资源ds.close();}
}
UDP接收端的使用步骤
UDP接收端的使用步骤1)创建接收端的Socketpublic DatagramSocket(int port) throws SocketException2)创建一个接收容器:数据报包 (并非真实数据)public DatagramPacket(byte[] buf,int length)参数1:缓冲区大小:长度1024/1024的倍数参数2:长度3)使用接收容器:接收数据public void receive(DatagramPacket p) throws IOException4)从缓冲区中获取真实数据(解析)public byte[] getData():实际字节数组public int getLength():实际长度5)展示...哪一个ip发送过来的数据 new String(实际字节数组,0,实际长度)6)接收端---->关闭接收端一般一直开启,不关闭并且-先启动接收端,在启动发送端!
public class UDP_Receive {public static void main(String[] args) throws IOException {//1)创建接收端的Socket//public DatagramSocket(int port) throws SocketExceptionDatagramSocket ds = new DatagramSocket(8888) ;//2)创建一个接收容器:数据报包 (并非真实数据)byte[] bytes = new byte[1024] ;int length = bytes.length ;DatagramPacket dp = new DatagramPacket(bytes,length) ;//3)接收数据ds.receive(dp);//4)解析容器中的数据//public byte[] getData():实际字节数组//public int getLength():实际长度byte[] buffer = dp.getData();int buffeLength = dp.getLength();//获取传递过来的数据String dataStr = new String(buffer,0,buffeLength) ;//获取ip地址字符串形式//public InetAddress getAddress()//public String gethostAddress()String ip = dp.getAddress().getHostAddress() ;System.out.println("data from --"+ip+",data is-->"+dataStr);//关闭ds.close();}
}
优化
优化:需要发送端,键盘录入数据(BufferedReader的readLine())不断录入数据,并且自定义结束标记"886",接数收端不关闭,不断的接收据udp一个窗口进行聊天,ChatRoom类 (简易版)开启两条线程发送端线程接收端的线程
public class SendTest {public static void main(String[] args) {//发送端的SocketDatagramSocket ds = null ;try {ds = new DatagramSocket() ;//数据报包:把数据放在包中//利用BufferedReader的readLineBufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;String line = null ;while((line=br.readLine())!=null){//结束标记if("886".equals(line)){break ;}//创建DatagramPacketInetAddress inetAddress = InetAddress.getByName("10.12.156.196");DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,inetAddress,10086) ;//发送ds.send(dp);}} catch (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {if(ds!=null){ds.close();}}}
}//接收端不关闭(真实场景)
public class ReceiveTest {public static void main(String[] args) {//创建接收端的Socket对象try {DatagramSocket ds = new DatagramSocket(10086) ;while(true){//创建接收容器byte[] bytes = new byte[1024] ;int length = bytes.length ;DatagramPacket dp = new DatagramPacket(bytes,length) ;//接收数据ds.receive(dp);//解析数据String dataStr = new String(dp.getData(),0,dp.getLength()) ;//获取ip地址String ip = dp.getAddress().getHostAddress() ;System.out.println("data from --->"+ip+",data is--->"+dataStr);}} catch (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {// //不关闭...}}
}
基于TCP的网络编程
开发步骤
开发步骤:建立通信连接(会话):创建ServerSocket,指定端口号调用accept等待客户端接入客户端请求服务器:创建Socket,指定服务器IP+端口号使用输出流,发送请求数据给服务器使用输入流,接受响应数据到客户端(等待)服务器响应客户端使用输入流,接受请求数据到服务器(等待)使用输出流,发送响应数据给客户端
TCP客户端的使用步骤
TCP客户端(需要建立连接通道) 使用步骤1)创建客户端所在的Socket对象public Socket(String host,int port)throws UnknownHostException,IOException参数1:主机名称/ip地址字符串表现形式参数2:端口号2)写数据到服务器端public OutputStream getOutputStream() throws IOException:获取连接通道内的输出流对象写数据write:字节3)释放资源关闭socket对象所在的系统资源
public class ClientDemo {public static void main(String[] args) throws IOException {//1)创建客户端所在的Socket对象Socket socket = new Socket("10.12.156.196",6666) ;//2)获取连接通道内的输出流对象// public OutputStream getOutputStream() throws IOException:OutputStream out = socket.getOutputStream();//写数据到流中out.write("hello TCP,我来了".getBytes());//3)释放资源socket.close();}
}
TCP服务端的使用步骤
TCP服务端的使用步骤:1)创建服务器端所在的Socket:此类实现服务器套接字public ServerSocket(int port) throws IOException:创建服务器端socket对象同时监听某个端口2)侦听客户端连接 (阻塞式方法:一旦客户端绑定的端口号和当前服务器端的端口不一致,永远等待..)public Socket accept() throws IOException :返回值就是被侦听到的客户端对象3)通过客户端获取通道内的输入流对象public InputStream getInputStream() throws IOException返回此套接字的输入流。要么一次读取一个字节/一次读取一个字节数组4)展示数据并且释放资源注意错误:启动一次,它就等待监听客户端;服务器端不要启动多次----BindException:绑定异常: Address already in user:端口号被占用!
public class ServerDemo {public static void main(String[] args) throws IOException {//1)创建服务器端所在的Socket:此类实现服务器套接字ServerSocket ss = new ServerSocket(6666) ;//2)侦听客户端连接Socket socket = ss.accept();//一旦侦听到客户端--获取通道内的输入流对象InputStream inputStream = socket.getInputStream();//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = inputStream.read(bytes); //获取字节数//展示接收到的数据String clientMsg = new String(bytes,0,len) ;//获取客户端ip地址//public InetAddress getInetAddress()InetAddress inetAddress = socket.getInetAddress();String ip = inetAddress.getHostAddress() ;System.out.println("data from--->"+ip+",data is-->"+clientMsg);//释放资源ss.close();}
}
加入响应
//客户端发送消息,服务器端接收消息之后展示,并且给客户端响应数据,客户端将响应的数据可以展示public class ClientDemo {public static void main(String[] args) throws IOException {//创建一个客户端的Socket对象Socket socket = new Socket("10.12.156.196",12306) ;//获取通道内输出流,给服务器端写过去OutputStream outputStream = socket.getOutputStream();outputStream.write("ServerSocket,你好".getBytes());//获取服务器响应的数据//获取通道内的输入流对象InputStream inputStream = socket.getInputStream();//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = inputStream.read(bytes) ;String responseMsg = new String(bytes,0,len) ;System.out.println("responseMsg:"+responseMsg);//关闭资源socket.close();}
}public class ServerDemo {public static void main(String[] args) throws IOException {//创建服务器端的Socket对象ServerSocket ss = new ServerSocket(12306) ;//监听Socket socket = ss.accept();//获取到客户端的通道内输入流对象InputStream in = socket.getInputStream();//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = in.read(bytes) ;String sendMsg = new String(bytes,0,len) ;System.out.println("data from--->"+socket.getInetAddress().getHostAddress()+",data is-->"+sendMsg);//服务器端响应给客户端数据//获取监听客户端所在的通道内输出流对象OutputStream outputStream = socket.getOutputStream();outputStream.write("socket,你好,数据已经是收到".getBytes());//释放资源ss.close();}
}
UDP和TCP协议的区别
UDP是一种不可靠连接,不安全---执行效率高
UDP不需要建立连接通道,通过一种数据报包(DatagramPacket)的形式,发送数据/接收数据
UDP有文件大小限制TCP是一种可靠连接,安全---执行效率低
TCP是需要建立连接通道,通过一种最基本的OutputStream/InputStream
TCP相对UDP没有具体的限制
题目
键盘录入…
//TCP客户端:不断键盘录入数据,服务器将数据复制到当前项目下的Copy.java文件中
public class ClientTest {public static void main(String[] args) throws IOException {//1)客户端Socket对象Socket socket = new Socket("10.12.156.196",8888) ;//2)键盘录入数据:使用BufferedReader的readLine:读取一行BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;//3)获取当前客户端所在通道内的输出流:OutputStream getOutputStream()// OutputStream out = socket.getOutputStream();//封装通道内的流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;String line = null ;while((line=br.readLine())!=null){if("over".equals(line)){break ;}//录入一行数据,将数据写入到封装的输出流中bw.write(line);bw.newLine();bw.flush();}//释放资源br.close();socket.close();}
}public class ServerTest {public static void main(String[] args) throws IOException {//服务器端是SocketServerSocket ss = new ServerSocket(8888) ;Socket socket = ss.accept();//封装通道内输入流BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;//创建字符缓冲输出流对象,输出指定文件copy.javaBufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt")) ;//一次读取一行数据String line = null ;while((line=br.readLine())!=null){//使用BufferedWriter写入到指定的文件中bw.write(line);bw.newLine();bw.flush();}bw.close();//服务器不关闭}
}
操作文本文件遇到阻塞问题
/*** TCP的客户端的操作ClientDemo.java文件* 需要使用BufferedReader进行读取,将文件内容写入到通道内的输出流中** 服务器端将文件内容获取到并且复制到当前项目下Copy.java文件中** 条件:* 文件复制完毕,服务器端给客户端进行反馈,客户端将反馈的消息打印出来!*** 问题:服务器端和客户端都正在执行,程序就是没有结束!** 原因:** 读取某个文本文件* readLine() 返回值为null,表示文件已经读取完毕 阻塞式方法* 返回值已经null,但是null不能用来判断通道内的输出是否已经写入完毕*** 文件已经读取完毕,服务器不知道!** 针对服务器端:需要通过BufferedReader来进行读取复制文件内容,readLine():返回为null,文件复制完毕,* 等待服务器反馈!*** 解决方案:* 1)在客户端写入完毕,自定义结束标记"886"/"over",服务器端读到这个标记的,结束!* (推荐) 2)public void shutdownOutput() throws IOException:在客户端写入完毕的时候,调用这个功能,告诉服务器,没有内容需要写入到流中******/
public class ClientTest {public static void main(String[] args) throws IOException {//客户端SocketSocket socket = new Socket("10.12.156.196",6666) ; //一旦创建对象创建完毕,已经和服务器端连接//创建字符缓冲输入流,读取指定的文件BufferedReader br = new BufferedReader(new FileReader("ClientDemo.java")) ;//封装通道内 的输出流对象,将文件内容进行写入到流对象汇总BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;String line = null ;while((line=br.readLine())!=null){ //readLine() 返回值为null,表示文件已经读取完毕 阻塞式方法//返回值已经null,但是null不能用来判断通道内的输出是否已经写入完毕bw.write(line);bw.newLine();bw.flush();}//告诉服务器端,文件写入完毕//自定义标记/* bw.write("over");bw.newLine();bw.flush();*///public void shutdownOutput() throws IOExceptionsocket.shutdownOutput();//接收服务器端的反馈//获取通道的内的输入流,读取//客户端也不知道服务器端是否将文件已经复制完毕BufferedReader br2 = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;String serverMsg = br2.readLine();System.out.println("serverMsg:"+serverMsg);//释放资源br.close();socket.close();}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class ServerTest {public static void main(String[] args) throws IOException {//服务器端是SocketServerSocket ss = new ServerSocket(6666) ;Socket socket = ss.accept();//阻塞式方法: 如果一旦某个客户端端口号跟主机的端口号一致,可以被服务器监听//封装通道内输入流BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;//创建字符缓冲输出流对象,输出指定文件copy.javaBufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;//一次读取一行数据String line = null ;while((line=br.readLine())!=null){ //阻塞式方法//使用BufferedWriter写入到指定的文件中//读取标记:/*if("over".equals(line)){break ;}*/bw.write(line);bw.newLine();bw.flush();}//服务器端的响应动作//继续获取通道内的输出流,写给客户端BufferedWriter bw2 = new BufferedWriter( //给客户端反馈new OutputStreamWriter(socket.getOutputStream())) ;bw2.write("服务器已经将数据复制完毕");bw2.newLine();bw2.flush();bw.close();//服务器不关闭ss.close();}
}
操作图片文件遇到图片缺失问题
/*** TCP客户端的有一个图片文件高圆圆.jpg,* 服务器端将图片内容进行复制---当前项目下:mv.jpg,并加入反馈操作(服务器端给客户端反馈!)** 图片复制完毕,但是缺失,对于缓冲的字节数,通过字节流也可以刷新的* public void flush()* throws IOException :强制刷新此缓冲区字节数!**/
public class UploadClient {public static void main(String[] args) throws IOException {//客户端Socket socket = new Socket("10.12.156.196",2222) ;//创建BufferedInputStreamBufferedInputStream bis = new BufferedInputStream(new FileInputStream("高圆圆.jpg")) ;//封装通道内的输出流BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = 0 ;while((len=bis.read(bytes))!=-1){//将内容写入到通道内的输出流中bos.write(bytes,0,len);bos.flush();}//通知:服务器端:当前流数据没有可写内容了socket.shutdownOutput();//读取反馈//获取通过内的输入流InputStream in = socket.getInputStream();byte[] bytes2 = new byte[1024] ;int len2 = in.read(bytes2) ;String severMsg = new String(bytes2,0,len2) ;System.out.println("serverMsg:"+severMsg);//释放资源bis.close();socket.close();}}public class UploadServer {public static void main(String[] args) throws IOException {//服务器端的SocketServerSocket ss = new ServerSocket(2222) ;Socket socket = ss.accept() ;//封装通道内输入流对象BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;//创建输出流 -----mv.jpgBufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mv.jpg")) ;//一次读取一个字节数组byte[] bytes = new byte[1024] ;int len = 0 ;while((len=bis.read(bytes))!=-1){bos.write(bytes,0,len);bos.flush();}//反馈//获取通道内输出流OutputStream out = socket.getOutputStream();out.write("图片已经复制完毕".getBytes());bos.flush();//释放资源bos.close();ss.close();}
}
反射
类的加载
类对象
类的对象:基于某个类new出来的对象,也称为实例对象类对象:类加载的产物,封装了一个类的所有信息(类名,父类,接口,属性,方法,构造方法)
类加载
类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载就是指将class文件读入内存,并为之创建一个Class对象。 任何类被使用时系统都会建立一个Class对象。
连接验证是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用
类初始化时机创建类的实例 访问类的静态变量,或者为静态变量赋值 调用类的静态方法 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 初始化某个类的子类 直接使用java.exe命令来运行某个主类
类加载器
类加载器 负责将.class文件加载到内在中,并为之生成对应的Class对象。 类加载器的组成 Bootstrap ClassLoader 根类加载器 也被称为引导类加载器,负责Java核心类的加载 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中Extension ClassLoader 扩展类加载器负责JRE的扩展目录中jar包的加载。 在JDK中JRE的lib目录下ext目录Sysetm ClassLoader 系统类加载器负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
什么是反射
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调 用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java代码经历的三个阶段
1)SORUCE:源码阶段
2)CLASS:编译阶段 ----->Class:字节码文件对象
3)RUNNABLE:运行阶段
获取一个字节码文件对象的方式有几种
三种:Object类的getClass()---->Class<T>任意Java类型的class属性--->Class<T>Class类中静态功能:forName("类的全限定名称") ;
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {//创建一个Person类对象Person p1 = new Person() ;//第一种方式获取字节码文件对象System.out.println(p1.getClass());//class com.qf_reflect_08.PersonSystem.out.println(p1.getClass().getName());//com.qf_reflect_08.PersonSystem.out.println("--------------------------");//第二种方式获取Class<Person> c = Person.class ;System.out.println(c);//class com.qf_reflect_08.PersonSystem.out.println("--------------------------");//public static Class<?> forName(String className) throws ClassNotFoundException//注意事项:参数为字符串:全限定名从:包名.类名Class c2 = Class.forName("com.qf_reflect_08.Person") ;System.out.println(c2);System.out.println(c2.getName());//com.qf_reflect_08.Person}
}
使用反射的方式创建某个类的实例
1)获取当前类的字节码文件对象:Class.forName("类的全限定名称")--->Class clazz
2)当前类的构造方法:无参/公共权限 :构造器对象所代表的的指定的公共的构造方法Constructor con = clazz.getConstructor() ;public 包名.类名类名()//如果是私有的构造方法/默认的/受保护的con.setAccessiable(true) ;
3)创建该类实例: con.newInstance()--->Object obj :当前该类的实例!public Constructor<?>[] getConstructors() throws SecurityException:获取公共的构造器对象
public Constructor<?>[] getDeclaredConstructors():获取的是所有的,包含私有,公共,受保护的,默认的.
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定的公共的构造器对象,
public T newInstance(Object... initargs)返回值就是当前类的实例 ,参数参数:需要给构造器中传递的实际参数,没有参数,空参
public class RefectTest {public static void main(String[] args) throws Exception {//1)获取当前Person类所在的字节码文件对象Class c = Class.forName("com.qf_reflect_08.Person") ;//class com.qf_reflect_08.Person//2)public Constructor<?>[] getConstructors() throws SecurityException:获取公共的构造器对象//public Constructor<?>[] getDeclaredConstructors():获取的是所有的,包含私有,公共,受保护的,默认的.
// Constructor[] con = c.getConstructors();// Constructor[] con = c.getConstructors();// Constructor[] con = c.getDeclaredConstructors() ;//遍历/* for(Constructor constructor:con){System.out.println(constructor);*//*公共的构造器对象* public com.qf_reflect_08.Person(java.lang.String)public com.qf_reflect_08.Person()所有的com.qf_reflect_08.Person(java.lang.String,int,java.lang.String)private com.qf_reflect_08.Person(java.lang.String,int)public com.qf_reflect_08.Person(java.lang.String)public com.qf_reflect_08.Person()* *///}//获取的单个的构造器对象: 公共的//public Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定的公共的构造器对象,//参数:形式参数类型 的字节码文件对象:类名.class
// Constructor con = c.getConstructor(); //空参构造对象Constructor con = c.getConstructor(String.class);//System.out.println(con);//public com.qf_reflect_08.Person()//通过构造器对创建Person类的实例//public T newInstance(Object... initargs)://返回值就是当前类的实例 ,参数参数:需要给构造器中传递的实际参数,没有参数,空参!
// Object obj = con.newInstance();
// System.out.println(obj);Object obj = con.newInstance("高圆圆") ;System.out.println(obj);System.out.println("---------------------------");Person p = new Person("高圆圆") ;System.out.println(p);}
}
访问私有成员
IllegalAccessException:非法访问异常:这个类不能访问Person私有的成员AccessibleObject 类是 Field、Method 和 Constructor 对象的基类public void setAccessible(boolean flag):参数为true:取消Java语言访问检查:暴力访问
/*** 通过私有的构造方法创建Person类的实例,并且给成员信息赋值*/
public class ReflectTest2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,IllegalAccessException, InvocationTargetException, InstantiationException {//获取Person类的字节码对象Class clazz = Class.forName("com.qf_reflect_08.Person");//2)获取指定的构造器对象所代表的构造方法//getDeclaredConstructor(Class<T>...parameterClass)//private Person(String name,int age):
// Constructor con = clazz.getDeclaredConstructor(String.class,int.class) ;Constructor con = clazz.getDeclaredConstructor(String.class,int.class,String.class) ;
// System.out.println(con);//private com.qf_reflect_08.Person(java.lang.String,int)con.setAccessible(true);//通过构造器对象所代表的指定的这个构造方法创建该类实例Object obj = con.newInstance("高圆圆", 30,"鄠邑区");//IllegalAccessException:非法访问异常:这个类不能访问Person私有的成员//AccessibleObject 类是 Field、Method 和 Constructor 对象的基类//public void setAccessible(boolean flag):参数为true:取消Java语言访问检查:暴力访问System.out.println(obj);//实例/*** Person p = new Person("高圆圆",30) ; //编译通过不了,私有的构造方法**/}
}
通过反射获取Field对象所代表的成员变量,并且为其赋值
public Field[] getFields():获取当前类/接口的公共字段
public Field[] getDeclaredFields():获取当前类/接口所声明的所有的字段public Field getDeclaredField(String name):获取指定的字段
public Field getField(String name):获取单个的公共的字段形式参数:就是当前字段名称public void set(Object obj,Object value): ----类似于:p.name = "高圆圆";参数1:当前类的实例,参数2:当前这个赋的实际值1)获取当前类的字节码文件对象:Class.forName("类的全限定名称")--->Class clazz//创建该类的实例clazz.newInstance()---->Object obj ---->Person p = new Person() ;2)clazz.getField("公共的字段名称") ;----->Field f1
3)直接赋值: f1.set(obj,"实际参数") ;
public class ReflectTest3 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {//1)获取当前Person类字节码文件对象Class personClazz = Class.forName("com.qf_reflect_08.Person");//2)public Field[] getFields():获取当前类/接口的公共字段//public Field[] getDeclaredFields():获取当前类/接口所声明的所有的字段
// Field[] fields = personClazz.getFields();
// Field[] fields = personClazz.getDeclaredFields();//// for(Field field:fields){// System.out.println(field);//public java.lang.String com.qf_reflect_08.Person.address/*** private java.lang.String com.qf_reflect_08.Person.name* int com.qf_reflect_08.Person.age* public java.lang.String com.qf_reflect_08.Person.address*///}//获取公共的构造器对象所代表的构造方法:无参/公共的Constructor con = personClazz.getConstructor();//通过构造器对象创建该类的实例Object obj = con.newInstance() ;//Person p = new Person() ;System.out.println(obj);//获取单个并且指定的构造方法://public Field getDeclaredField(String name):获取指定的字段//public Field getField(String name):获取单个的公共的字段//形式参数:就是当前字段名称Field nameField = personClazz.getDeclaredField("name");//public void set(Object obj,Object value): ----类似于:p.name = "高圆圆";//参数1:当前类的实例,参数2:当前这个赋的实际值nameField.setAccessible(true);//取消Java语言访问检查nameField.set(obj,"高圆圆"); //name私有的字段System.out.println(obj);System.out.println("-----------------------------");//获取指定的age字段Field ageField = personClazz.getDeclaredField("age");ageField.setAccessible(true);ageField.set(obj,30);System.out.println(obj);System.out.println("-----------------------------");//获取address字段:公共的Field addressField = personClazz.getField("address");addressField.set(obj,"鄠邑区");System.out.println(obj);System.out.println("---------------------------------------");//类对象的创建----Class.forName("全限定名名称")----> Constructor 创建该类实例//类对象的创建----Class.forName("全限定名名称")----> 直接创建该类实例//public T newInstance()Class clazz = Class.forName("com.qf_reflect_08.Person");System.out.println(clazz.newInstance()); //Person{name='null', age=0, address='null'}}
}
通过反射获取Method对象所代表的成员方法并调用这个方法
public Method[] getMethods():获取当前类/接口中的所有公共的成员方法,包括父类/父接口public Method[] getDeclaredMethods():获取当前类/接口执行的成员方法:公共的,私有的,默认的,受保护的获取单个并且指定的公共的构造方法并调用
public Method getMethod(String name,Class<?>... parameterTypes)参数1:方法名称参数2:当前这个方法的形式参数类型的class属性 举例:带一个String类型的参数:String.class调用方法:底层的调用方式public Object invoke(Object obj,Object... args)参数1:当前类/接口的实例参数2:是对方法的形式参数赋值返回值:如果当前方法有返回结果,返回,没有的话,直接单独调用1)获取当前类的字节码文件对象:Class.forName("类的全限定名称")--->Class clazz//创建该类的实例clazz.newInstance()---->Object obj Person p = new Person() ;
2)获取指定的公共的Method对象所代表的的成员方法//clazz.getMethod("方法名",形式参数类型的class) ---->p.show() ;clazz.getMethod("show",String.class) ;---->Method m
3)调用这个方法:invoke(当前类的实例,如果有形式参数--赋值实际参数) m.invoke(obj,"hello,javaEE") ; :如果当前成员方法有返回值类型,这个时候调用的时候Object o = m.invoke(obj,"hello,javaEE") ;
public class ReflectTest4 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {//1)获取Person类的字节码文件对象Class personClazz = Class.forName("com.qf_reflect_08.Person") ;//2)public Method[] getMethods():获取当前类/接口中的所有公共的成员方法,包括父类/父接口//2)public Method[] getDeclaredMethods():获取当前类/接口执行的成员方法:公共的,私有的,默认的,受保护的
// Method[] methods = personClazz.getMethods();/* Method[] methods = personClazz.getDeclaredMethods();for(Method method:methods){System.out.println(method);}*/Constructor con = personClazz.getConstructor() ;Object obj = con.newInstance() ;//Person p = new Person() ;//获取单个并且指定的公共的构造方法并调用//public Method getMethod(String name,Class<?>... parameterTypes)//参数1:方法名称//参数2:当前这个方法的形式参数类型的class属性 举例:带一个String类型的参数:String.classMethod m1 = personClazz.getMethod("show");//p.show() ;//调用方法:底层的调用方式//public Object invoke(Object obj,Object... args)//参数1:当前类/接口的实例//参数2:是对方法的形式参数赋值//返回值:如果当前方法有返回结果,返回,没有的话,直接单独调用m1.invoke(obj) ;System.out.println("---------------------------------------------");//调用method():私有的Method m2 = personClazz.getDeclaredMethod("method", String.class);
// System.out.println(m2);//取消Java语言访问检查m2.setAccessible(true);m2.invoke(obj,"Mysql..") ;System.out.println("-------------------------------------");//function()调用Method m3 = personClazz.getMethod("function");Object object = m3.invoke(obj);//返回的结果System.out.println(object);}
}
反射应用举例
1.通过配置文件(name.txt)运行类中的方法
1. 反射应用Student类中有love方法Teacher类中有love方法随着需求不断变化---现有一个.txt的配置文件:name.txtclassName=包名.类名methodName=love使用所学的东西将文件读取并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法
import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Properties;public class Test02 {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Properties prop = new Properties();prop.load(new FileReader("name.txt"));String classname = prop.getProperty("className");String methodname = prop.getProperty("methodName");Class clas = Class.forName(classname);Constructor con = clas.getConstructor();Object obj = con.newInstance();Method m = clas.getDeclaredMethod(methodname);m.invoke(obj);}}public class Student {public void love(){System.out.println("love Java...");}
}
public class Teacher {public void love(){System.out.println("love 高圆圆...");}
}
2,通过配置文件(name.properties)运行类中的方法
2.读取name.properties配置文件,并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法xxx.properties/xxx.xml/xxx.yml(springboot)
都放在src目录下:类路径下1)获取当前类的字节码文件对象2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器3)ClassLoader:类加载器public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象4)创建Properties属性集合列表对象load(InputStream in)5)获取key对应的value---反射创建实例,调用方法....public class Test2 {public static void main(String[] args) throws Exception {// 1)在哪个类下去读取这个配置文件:获取当前类的字节码文件对象Class clazz = Test2.class ;
// 2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器ClassLoader classLoader = clazz.getClassLoader();//3) public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象InputStream inputStream = classLoader.getResourceAsStream("name.properties");//4)创建Properties属性集合列表对象Properties prop = new Properties() ;//加载prop.load(inputStream);
// System.out.println(prop);String className = prop.getProperty("className");String methodName = prop.getProperty("methodName");//反射创建该类实例Class c = Class.forName(className) ;Object obj = c.newInstance();//获取Method对象--调用Method method = c.getMethod(methodName);method.invoke(obj) ;}
}
3.ArrayList,有一些字符串元素,如何给ArrayList中添加Integer元素呢?
public class Test1 {public static void main(String[] args) throws Exception {//创建ArrayList集合对象ArrayList<String> array = new ArrayList<String>() ; //明确数据类型:防止运行时期异常array.add("hello") ;array.add("world") ;array.add("java") ;//array.add(100) ; //直接添加不了//通过反射的方式去完成//获取ArrayList集合的字节码文件对象Class clazz = array.getClass();// System.out.println(clazz);//class java.util.ArrayList//通过反射的方式:调用public boolean add(E e) {}//通过反射获取Method对象所代表的的成员方法//getMethod("方法名",形式参数类型的class属性)---->Method mMethod method = clazz.getMethod("add", Object.class) ;//调用这个方法method.invoke(array,100) ;method.invoke(array,10) ;method.invoke(array,30) ;System.out.println(array);}
}
动态代理
代理模式
代理模式:静态代理----- Thread implements Runnable接口真实角色和代理角色:实现同一个接口动态代理----->在程序执行过程中:产生的代理对象代理角色---对当前真实角色的功能进行增强!jdk动态代理:基于接口cglib动态代理:基于子类
jdk动态代理
java.lang.reflect.Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。---->产生代理角色public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)参数1:代理类的加载器参数2:代理类要实现的接口列表参数3:代理实例的处理程序返回结果:带有代理类的指定调用处理程序的代理实例mybatis 里面动态代理---实质就是Proxy作为父类间接实现代理
public class Test {public static void main(String[] args) {//接口多态UserDao ud = new UserDaoImpl() ; //目标角色ud.add();ud.delete();ud.update();ud.find();System.out.println("---------------------------------");//优化:需要使用jdk动态代理:在程序运行的时候,产生代理角色---UserDao的代理角色//获取基于代理处理程序---产生的代理类UserDao ud2 = new UserDaoImpl() ;//接口多态InvocationHandler handler = new MyInvocationHandler(ud2) ;UserDao udProxy = (UserDao) Proxy.newProxyInstance(ud2.getClass().getClassLoader(),ud2.getClass().getInterfaces(), handler);//udProxy----代理角色udProxy.add();udProxy.delete();udProxy.update();udProxy.find();}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;//代理的处理程序
public class MyInvocationHandler implements InvocationHandler {//声明目标角色private Object target ; //UserDaopublic MyInvocationHandler(Object target){this.target = target ;}//对真实角色进行增强//method----基于代理的底层方法:add,delete,update,find...//args:参数--->这些方法的 对象数组@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("权限校验...");Object obj = method.invoke(target, args); //代理角色System.out.println("产生日志记录");return obj;}
}
//持久层接口---用户操作
public interface UserDao {void add() ;//添加void update(); //修改void delete() ;//删除void find() ;//查询}
//接口的实现层
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("添加用户...");}@Overridepublic void update() {System.out.println("修改用户...");}@Overridepublic void delete() {System.out.println("删除用户...");}@Overridepublic void find() {System.out.println("查询用户...");}
}
p.getProperty(“methodName”);
Class clas = Class.forName(classname);Constructor con = clas.getConstructor();Object obj = con.newInstance();Method m = clas.getDeclaredMethod(methodname);m.invoke(obj);}
}
public class Student {
public void love(){System.out.println("love Java...");
}
}
public class Teacher {
public void love(){System.out.println("love 高圆圆...");
}
}
#### 2,**通过配置文件(name.properties)运行类中的方法**```java
2.读取name.properties配置文件,并获取文件中的键对应的值,然后使用反射创建该类实例,调用方法xxx.properties/xxx.xml/xxx.yml(springboot)
都放在src目录下:类路径下1)获取当前类的字节码文件对象2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器3)ClassLoader:类加载器public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象4)创建Properties属性集合列表对象load(InputStream in)5)获取key对应的value---反射创建实例,调用方法....public class Test2 {public static void main(String[] args) throws Exception {// 1)在哪个类下去读取这个配置文件:获取当前类的字节码文件对象Class clazz = Test2.class ;
// 2)通过当前类的字节码文件对象:public ClassLoader getClassLoader() 当前这个类的加载器ClassLoader classLoader = clazz.getClassLoader();//3) public InputStream getResourceAsStream(String name):获取当前路径下中指定的资源文件的输入流对象InputStream inputStream = classLoader.getResourceAsStream("name.properties");//4)创建Properties属性集合列表对象Properties prop = new Properties() ;//加载prop.load(inputStream);
// System.out.println(prop);String className = prop.getProperty("className");String methodName = prop.getProperty("methodName");//反射创建该类实例Class c = Class.forName(className) ;Object obj = c.newInstance();//获取Method对象--调用Method method = c.getMethod(methodName);method.invoke(obj) ;}
}
3.ArrayList,有一些字符串元素,如何给ArrayList中添加Integer元素呢?
public class Test1 {public static void main(String[] args) throws Exception {//创建ArrayList集合对象ArrayList<String> array = new ArrayList<String>() ; //明确数据类型:防止运行时期异常array.add("hello") ;array.add("world") ;array.add("java") ;//array.add(100) ; //直接添加不了//通过反射的方式去完成//获取ArrayList集合的字节码文件对象Class clazz = array.getClass();// System.out.println(clazz);//class java.util.ArrayList//通过反射的方式:调用public boolean add(E e) {}//通过反射获取Method对象所代表的的成员方法//getMethod("方法名",形式参数类型的class属性)---->Method mMethod method = clazz.getMethod("add", Object.class) ;//调用这个方法method.invoke(array,100) ;method.invoke(array,10) ;method.invoke(array,30) ;System.out.println(array);}
}
动态代理
代理模式
代理模式:静态代理----- Thread implements Runnable接口真实角色和代理角色:实现同一个接口动态代理----->在程序执行过程中:产生的代理对象代理角色---对当前真实角色的功能进行增强!jdk动态代理:基于接口cglib动态代理:基于子类
jdk动态代理
java.lang.reflect.Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。---->产生代理角色public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)参数1:代理类的加载器参数2:代理类要实现的接口列表参数3:代理实例的处理程序返回结果:带有代理类的指定调用处理程序的代理实例mybatis 里面动态代理---实质就是Proxy作为父类间接实现代理
public class Test {public static void main(String[] args) {//接口多态UserDao ud = new UserDaoImpl() ; //目标角色ud.add();ud.delete();ud.update();ud.find();System.out.println("---------------------------------");//优化:需要使用jdk动态代理:在程序运行的时候,产生代理角色---UserDao的代理角色//获取基于代理处理程序---产生的代理类UserDao ud2 = new UserDaoImpl() ;//接口多态InvocationHandler handler = new MyInvocationHandler(ud2) ;UserDao udProxy = (UserDao) Proxy.newProxyInstance(ud2.getClass().getClassLoader(),ud2.getClass().getInterfaces(), handler);//udProxy----代理角色udProxy.add();udProxy.delete();udProxy.update();udProxy.find();}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;//代理的处理程序
public class MyInvocationHandler implements InvocationHandler {//声明目标角色private Object target ; //UserDaopublic MyInvocationHandler(Object target){this.target = target ;}//对真实角色进行增强//method----基于代理的底层方法:add,delete,update,find...//args:参数--->这些方法的 对象数组@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("权限校验...");Object obj = method.invoke(target, args); //代理角色System.out.println("产生日志记录");return obj;}
}
//持久层接口---用户操作
public interface UserDao {void add() ;//添加void update(); //修改void delete() ;//删除void find() ;//查询}
//接口的实现层
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("添加用户...");}@Overridepublic void update() {System.out.println("修改用户...");}@Overridepublic void delete() {System.out.println("删除用户...");}@Overridepublic void find() {System.out.println("查询用户...");}
}
5_异常_多线程_设计模式_IO流_网络编程_反射相关推荐
- Java 语言基础(异常机制和File类,IO流,多线程,网络编程,反射机制)
原文:Java 语言基础(异常机制和File类,IO流,多线程,网络编程,反射机制) 异常机制和File类 异常机制 基本概念 异常就是"不正常"的含义,在 Java 语言中主要指 ...
- Java的IO流与网络编程
目录 一.概述 二.文件类(File) 1. File类的构造.获取属性 2. File类获取子文件或目录 3. File类文件重命名 4. File类的判断功能 5. File类创建.删除功能 三. ...
- JAVA的IO流 和网络编程 超详细每行代码都有备注
IO流: { 文件的操作: f1.getAbsolutePath();//获取绝对路径 f1.getPath();//获取相对路径 f1 ...
- java基础知识总结:基础知识、面向对象、集合框架、多线程、jdk1.5新特性、IO流、网络编程
目录 一.基础知识: 二.面向对象 三.集合框架 四.多线程: 五.jdk1.5的新特性 六.IO流 七.网络编程: 一.基础知识: 1.JVM.JRE和JDK的区别: JVM(Java Virtua ...
- java io 网络编程_[笔面] Java IO和网络编程相关面试
1.网络编程时的同步.异步.阻塞.非阻塞? 同步:函数调用在没得到结果之前,没有调用结果,不返回任何结果. 异步:函数调用在没得到结果之前,没有调用结果,返回状态信息. 阻塞:函数调用在没得到结果之前 ...
- 西工大java高级网络编程_西工大16春《JAVA高级网络编程》平时作业
西工大16春<JAVA高级网络编程>平时作业 7 o& [9 w ^# D Z一.单选题:[25道,总分:100分]5 z# ?* Z! M% M1 h4 R# n. D+ a ...
- java scoket网络编程_嵌入式狗的JAVA之路 socket 网络编程
又前进一步,进入网络编程. 虽然很多内容没有深入,不过近期还是追求尽量多吸收知识吧,然后在具体项目中深入. IP 端口 定义 TCP UDP通信 http 80 ftp 21 telnet 23 ja ...
- 异常、File、IO、网络编程、反射
文章内容输出来源:拉勾教育大数据开发高薪训练营 第一章 异常机制 1.1 异常概念 异常,就是不正常的意思.在生活中,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中: ...
- 缓冲流,转换流,序列化流,网络编程
一,缓冲流 能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等.这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,相当于是对基本流对象的一种增强.1.1 概述 缓冲 ...
最新文章
- 【通俗理解线性代数】 -- 理解二次型
- java主动抛出400异常_400个线程同时查询数据,抛出一个异常
- 正则表达式——获取指定IP的物理地址
- mysql存储引擎简介
- 宝塔清mysql主从日志_宝塔面板Mysql主从日志文件mysql-bin文件清除方法
- 五边形lisp程序_CAD LISP 程序
- 钢琴块2电脑版_快陪练教师端电脑版下载_快陪练教师端pc版免费下载[在线教学]...
- 【Python3爬虫】大众点评爬虫(搞定CSS反爬)
- 理财产品利息可每天提取吗?
- python-pycharm使用方法
- tar和其他一些压缩解压工具
- python谷歌翻译 频率限制_利用Google进行无长度限制的文本翻译(无需API,无需Money)...
- ARCGIS小工具(插件)免费版_自取_GIS插件_工具_其他
- 八爪鱼采集器使用方法
- 怀孕计算机在线,【怀孕天数计算器在线计算_怀孕天数计算器在线计算专题】- 天鹅到家...
- 大白菜重装完系统后出现双系统怎么办
- snipaste 方便快捷截图工具
- 应用程序正常初始化(0xc0000005)失败
- python更新excel内容_[原创]使用 Python 读写 Excel 文件(一)更新
- android 远距离识别,远距离 人脸识别!