目录
2.2 Map集合和Collection集合的区别?(面试题)
3.10 使用两种方式分别去模拟电影卖票案例---多线程的安全问题(重点)
3.11 多线程安全问题解决--->效率低--->死锁 (生产者和消费者必须使用同一个资源)(重点)
3.12 什么是同步方法?(非静态) 以及静态同步方法他们说锁对象是谁?
3.13 wait()/notify()并非Thread类中为什么Object的?
3.14 什么死锁?如何解决死锁----"生成者消费者模式思想"
3.15 jdk5提供了根据的锁操作---Lock(接口)和synchronized同义
1.TreeSet(比较器排序)
按照TreeSet的构造方法,分为两种排序方式,1.自然排序,2.比较器排序
public TreeSet(Comparator<E> comparator)创建TreeSet集合使用比较器进行排序,需要存储的类型实现Comparator接口中的int compare(T o1,T o2),T--Type类型,需要Compartor中的泛型一致!
//创建TreeSet集合对象 //public TreeSet(Comparator<E> comparator) //方案1:自定义一个类 实现Comparator接口 //接口多态/创建具体的子实现类对象 // MyComparator my = new MyComparator() ; //TreeSet<Student> ts = new TreeSet<>(my) ; //创建一个类去重写 Comparator接口里面的compare方法 public class MyComparator implements Comparator<Student> { //重写接口中的compare方法 @Override public int compare(Student s1, Student s2) { //主要条件: // 按照学生的年龄从小到大排序 //s1--->相当于自然排序里面 this //s2--->相当于自然排序里面s int num = s1.getAge() - s2.getAge() ; //次要条件:年龄相同的人,按照姓名进行比较:(字典顺序比较) int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ; return num2; } }
2.Map<K,V>集合的功能以及遍历方式
2.1 引入Map<K,V>集合
需求:根据学生的编号Id,获取指定的学生的姓名 * 之前Collection<E>/List<E>,E--存储对应的类型,学生类中将id和姓名设置为属性!想要通过id获取指定的学生姓名,通过单列集合 * 没有办法直接操作,Java提供了一个键值对Map集合 引入Map<K,V>集合 * Map<K,V> :一个键对应一个值(键值映射),Map集合针对键必须唯一,值是可以重复的! K-->Integer V ---String 1 张三 2 王五 3 高圆圆 4 文章 1 赵又廷 --键重复,后面的值将前面的值覆盖
2.2 Map集合和Collection集合的区别?(面试题)
1)存储类型 Collection<E>,只能存储一种引用类型---属于"单列集合"---->理解为"光棍" Map<K,V>,可以存储多种类型的引用类型--->属于"双列集合"----理解为"夫妻对" 2)关系区别 Collection和Map没有直接关系,间接关系 Collection具体的子接口Set--->HashSet/Treeset 直接的去依赖于Map接口的HashMap和TreeMap
2.3 Map集合的基本功能
V put(K key,V value) :添加方法---返回值什么意思? 通过返回值是否为null,判断键是否重复,如果不重复,返回值永远是null; 如果键重复,将第一次存储键对应的值返回,将后面键对应的值进行存储 void clear():清空Map集合的所有键值对 V remove(Object key):删除Map集合的指定键,然后返回被删除的值 boolean containsKey(Object key):是否包含指定的键 boolean containsValue(Object value):是否包含指定的值 boolean isEmpty():判断Map集合是否为空
2.4 Mpa集合的两种遍历方式
第一种: * 1)Set<K> keySet()获取Map集合的所有的键的集合 * V get(Object key):通过键获取值 */ Set<String> keySet = map.keySet(); for(String key:keySet){ String value = map.get(key); System.out.println(key+"="+value); } //遍历大集合 //获取所有的键 Set<String> keySet = hm.keySet(); //遍历所有键 for(String key:keySet){ System.out.println(key+"\t") ; //通过键获取值---ArrayList<String> ArrayList<String> keyValue = hm.get(key); //遍历ArrayList for(String value:keyValue){ System.out.println("\t"+value); } }
/** * 第二种方式遍历 * Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象 * K getKey()获取键 * V getValue() */ //获取所有的键值对对象 Set<Map.Entry<String, String>> entrySet = map.entrySet(); for(Map.Entry<String,String> en:entrySet){ //获取键,获取值 String key = en.getKey(); String value = en.getValue(); System.out.println(key+"="+value) ; }
2.5 HashMap
/** * HashMap<Student,String> * Key:Student类型 ---学生信息 * Value:String类型 ---朝代 * 使用HashMap集合保证key唯一! * * HashMap--->底层哈希表实现,put方法依赖hashCode和equals方法, * Map针对键有效,要针对键的类型自定类型,当前类必须重写equals和hashCode(),保证键唯一(重点) */
2.6 Collections:针对单例集合的工具类
/** * java.util.Collections:针对集合操作工具类 * 提供一些常用的方法: * public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key): * 二分搜索在List中查询元素 (不管集合还是Arrays---->binaraySearch方法,(集合或者数组必须有序)) * public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) :获取Colection中的最大值 * public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) :获取Colection中的最小值 * public static <T extends Comparable<? super T>> void sort(List<T> list):针对List集合进行自然排序 * public static <T> void sort(List<T> list,Comparator<? super T> c):针对List集合比较器排序 * public static void shuffle(List<?> list):针对List集合随机置换 * * public static <T> List<T> synchronizedList(List<T> list):将线程不安全ArrayList,变成线程安全的列表 */
3 线程
3.1 进程和线程的概念
线程是依赖于进程的,没有进程就没有线程! 进程 在计算机的任务管理查看到此电脑上所有的客户端的进行! 概念: 调用"系统资源"的独立单位! 开启一个进程----创建 "系统资源" 所有的计算机,包括我们"jvm",都支持多进程,多进程的意义? 一句话:提高CPU的使用率! 在打游戏的同时,听音乐,---同时开启了两个进程,是同时进行的吗? 并不是同时的,CPU(时间片),一点点时间片可以高效在两个进制来回切换! 线程: 是进程中的最小执行单元!(理解为,进程中的某个任务) 举例: 360进程开启之后,可以杀毒/清理/电脑瘦身等等 多线程:在一个程序的过程中,其执行路径有多条,线程的执行具有"随机性"! 多线程的意义? 多个线程"共享同一个资源",互相抢占CPU执行权,从而达到线程的执行速度! 举例 1 v 3 打篮球,3个人抢占的篮球的几率大,可能这1个人也能抢占大篮球!
3.2 jvm是多线程的吗?(面试题)
是多线程;至少有两条件线程 * 1)main---"用户线程" * 2)垃圾回收线程,回收没有更多的引用对象,从而释放空间;如果没有 * 垃圾回收线程,Java中不断的去new,不断的堆内存空间---->导致OOM(Out Of Memory:内存溢出)
3.3 Java能创建线程吗?(面试题)
创建线程--->创建进程---->创建系统资源---->Java不能够直接创建资源的,间接的提供了一个Thread
3.4 创建线程的两种方式
* 创建线程的方式1:
* 1)自定义一个类 继承Thread类
* 2)重写Thread类的run方法
* 3)在main()用户线程中,创建当前线程了对象
* 4)启动线程--->start()不是run方法,run方法是一个普通方法,
*
* Thread类提供方法:
* public final String getName()获取线程的名称
* public final void setName(String name):设置线程名称
*/
/**
* 线程的创建方式2:
* 1)自定义一个类,实现Runnable接口
* 2)重写Runnable接口的run方法
* 3)在用户线程main中去创建当前类对象--->"资源类"
* 4)创建Thread类对象,将3)资源类作为Thread类对象的参数进行传递
*/
//Thread类静态方法
//public static Thread currentThread():获取正在执行的线程
System.out.println(Thread.currentThread().getName()+":"+x) ;
3.5throw和throws的区别(面试题)
异常处理
* throws:抛出
* throw:抛出
*
* 区别:
* 1)书写位置的区别
* throws:用在方法声明上
* throw:用在方法体中
* 2)后面跟的类型不一样的
* throws后面跟的异常类名,可以多个,中间逗号隔开
* throw:后面只能一个异常对象,new XXException()
* 3)是否出现异常的时机
* throws:可能出现问题,可能性
* throw:执行某个逻辑代码,一定会出现这个异常!(肯定性)
* 4)处理异常:
* throws:抛出给调用者,谁调用这个方法(带有throws的方法),谁必须对这个已处理(throws/try...catch...)
* throw:异常的处理:交给方法体中逻辑判断,if(xxx){throw new XXException(...);}
3.6 Java中异常的处理
/**
* 程序中会出现异常,开发人员必须解决异常!
* 异常的父类:Throwable
* -->提供一些方法:将异常的信息直接打印在控制上:具体(跟踪堆栈-->底层原码)
* public void printStackTrace()
*
* Error(严重问题) Exception
*
* 严重问题:内存溢出(借助于其他硬件解决这个问题,加内存条)
* Exception:
* 编译时期异常:只要不是RuntimeException的子类,都属于编译时期
* 必须让开发人员必须处理,不处理,代码编译通过不了!
* 运行时期异常:RuntimeException
* 大部分逻辑不够严谨,导致出现的问题
* NullPointerEception:在获取到某个类的对象的时候,并没有非空判断,
* 可能对象是null,使用这个对象调用它里面的方法,出现空指针了
* 在Java中处理异常方案:
* throws:抛出
* //标准格式
* try{
* 可能出现问题的代码
* }catch(异常类名 对象名){
* 处理异常
* }finally{
* //释放资源
* }
*
* 变形格式
* try{
* 可能出现问题的代码
* }catch(异常类名 对象名){
* 处理异常
* }
*
*
* try{
* 可能出现问题的代码
* }catch(异常类名 对象名){
* 处理异常
* }catch(异常类名2 对象名2){
* 处理异常...
* }
*/
//处理方案:
//try...catch...
//try代码如果出现问题,---执行catch语句---->处理异常---->内存中,jvm会创建异常的对象,new XXException,
//执行的catch语句代码
简单来说就是两种处理方案:
1.是抛出 throws:抛出,抛给调用者,谁调用谁处理
2.是捕获 try,,,catch,,,
3.7 异常的使用的注意事项
/**
* 异常的使用的注意事项:
* 1)子类继承父类的时候,如果子类重写父类的方法,父类的方法如果本身没有throws抛出异常
* 子类的方法中出现异常,子类只能自己处理,只能try...catch...
*
* 2)子类继承父类,子类重写父类方法的时候,如果父类的方法抛出异常,
* 那么子类的该方法最起码要根父类的方法异常保持一致(要么子类该放的异常是父类方法的异常中的子类)
*/
3.8 Thread线程提供的一些方法
底层源码中看到方法被native修饰,表示本地方法,表示该方法的实现由非java语言实现,比如C
join()-----等待线程终止
/**
* 等待该线程中终止
*public final void join() throws InterruptedException
* 因为线程的执行具有随机性,哪个线程启动之后,调用join(),正常理想情况,等待该线程执行完毕,执行其他线程!
* 需求
* 实现三个线程依次先后执行完毕, t1,t2,t3每一个线程启动之后,都可以调用join()
*/
/**
* public static void yield():线程礼让(暂停当前正在执行的线程,执行其他线程)
*/
/**
* public final void setPriority(int newPriority):设置优先级
* public final int getPriority():获取优先级
*
* Thread提供三个自定义常量:
* public static final int MAX_PRIORITY 10 最大优先级
* public static final int MIN_PRIORITY 1 最小优先级
* public static final int NORM_PRIORITY 5 默认优先级
* 创建线程,没有设置优先级,线程的默认优先级就是5(抢占CPU执行权的概率一样大)
*
*
*/
yield() ;//每一个线程在执行这个方法的时候,都要暂停当前自己的线程,执行其他线程
线程的生命周期
3.9 静态代理(重点)
/**
* 设计模式一种思想(前辈总结出来的),并非一种技术
* 代理设计模式属于结构型设计模式一种;
* 代理设计模式: 核心思想:让代理角色帮助真实角色完成一件事情!
* 静态代理
* 动态代理:
* jdk动态代理:前提必须有接口
* cglib动态代理:第三方jar包 ---基于子类实现
*
* 静态代理:
* 最大特点:代理角色和真实角色必须实现同一个接口
* 代理角色:帮助真实角色完成一些事情
* 真实角色:专注于自己的事情
*
举例:
* 举例:
* 结婚这件事情
*/
//测试类
public class StaticProxyDemo {
public static void main(String[] args) {
//测试真实角色
//接口多态/创建具体的子类
You you = new You() ;
you.mary();
System.out.println("---------------------------------") ;
//创建WeddingCompany类对象
You you2 = new You() ;//---->类似 资源类对象 MyRunnable implement Runnble
WeddingCompany weddingCompany = new WeddingCompany(you2) ; //类似 ---Thread t1 = new Thread(MyRunnable对象,线程名称);
weddingCompany.mary() ; //类似----t1.start() ; jvm调用run
}
}
//结婚的接口
interface Mary{
void mary() ;
}
//真实角色
class You implements Mary{
@Override
public void mary() {
System.out.println("要结婚了,很开心...");
}
}
//婚庆公司:代理角色
class WeddingCompany implements Mary{
//声明Mary接口变量
private Mary mary ; //---类似Thread类的private Runnable target;
public WeddingCompany(Mary mary){//类似 Thread(Runnable target,String name)
this.mary = mary ;
}
@Override
public void mary() {
if(mary!=null){
System.out.println("婚庆公司,要布置婚礼现场!");
//真实角色完成它自己的事情
mary.mary();
System.out.println("婚庆公司接收尾款!");
}
}
}
3.10 使用两种方式分别去模拟电影卖票案例---多线程的安全问题(重点)
/方式一
package com.qf.thread_06;
/**
* 自定义一个类继承Thread类
*/
public class SellTicket extends Thread{
//同时出售100张门票
//定义一个成员变量 -被共享
private static int tickets = 100 ;
@Override
public void run() {
//为了模拟一直有票
while(true){
if(tickets>0){
//模拟网络延迟,线程睡眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
}
//++或者-- 原子性操作(对变量本身最简单最直接的操作)
//记录之前的值,tickets,然后在进行自增或者自减
}
}
}
package com.qf.thread_06;
/**
* 需求:
* 使用线程创建的方式1,来实现,电影院有三个窗口,同时出售100张门票
*
*
*模拟真实的网络延迟,让线程的睡眠
*/
public class SellTicketTest {
public static void main(String[] args) {
//创建三个线程,分别代表三个窗口
SellTicket st1 = new SellTicket() ;
SellTicket st2 = new SellTicket() ;
SellTicket st3 = new SellTicket() ;
st1.setName("窗口1") ;
st2.setName("窗口2") ;
st3.setName("窗口3") ;
//分别启动线程
st1.start() ;
st2.start() ;
st3.start() ;
}
}
//方式二:
package com.qf.thread_07;
/**
* 自定义一个类实现Runnable接口
*/
public class SellTicket implements Runnable{
//同时出售100张门票
//定义一个成员变量 -被共享
private static int tickets = 100 ;
@Override
public void run() {
//为了模拟一直有票
while(true){
if(tickets>0){
//模拟网络延迟,线程睡眠100毫秒
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
}
package com.qf.thread_07;
/**
* 需求:
* 使用线程创建的方式2,来实现,电影院有三个窗口,同时出售100张门票
*
*
*模拟真实的网络延迟,让线程的睡眠
*/
public class SellTicketTest {
public static void main(String[] args) {
//创建SellTicket对象
SellTicket st = new SellTicket() ;
//创建三个线程
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
3.11 多线程安全问题解决--->效率低--->死锁 (生产者和消费者必须使用同一个资源)(重点)
/**
* synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
* 多条语句对共享数据的操作
*
* }
*/
//t1,t2,t3
// synchronized (new Object()){//必须同一把锁
//t1线程抢占到了,t2,t3进不来的,t1将这个同步代码块内容执行,出售第100张
//--->t1,t2,t3在继续互相抢占---t3进来了,t2和t1进不来的
/**
* 负票的出现:(睡眠动作以及线程执行随机性)
* tickets-- :记录以前的值,在进行--动作(原子性操作)
* 窗口1正在出售1张票,tickets=1 ,记录以前的1的值,然后在进行--动作
* 在准备--,第二个线程或者第三个线程互相抢,窗口2先抢占到了,先睡眠了醒来了,窗口2正在出售第0张票
* 在准备--的时候,t1线程抢占到了,睡醒来,窗口1正在出售第-1张票
*
*/
/**
* 需求:
* 使用线程创建的方式2,来实现,电影院有三个窗口,同时出售100张门票
*
*
*模拟真实的网络延迟,让线程的睡眠
* 虽然使用方式2(静态代理)进行实现,也依然出现了问题
* 1)同一张票被多次 --->线程的执行随机性
* 2)负票的出现 --->加入睡眠以及原子性操作(--或者--)
*
* 上面2种情况,都是多线程的安全问题
*
* 校验多线程安全问题的标准 (解决方案)
* 1)检查程序是否是多线程环境 :是多线程卖票
* 2)是否存在共享数据 : 也存在的而且存在100张票
* 3)是否存在多条语句对共享数据的操作:也存在,判断tickets以及完成--
*
* Java提供了同步代码块,解决上面3),使用同步代码块将多条语句对象共享数据的包起来
* Java的同步机制
* synchronized(锁对象){ //锁对象可以是任意的Java类对象,每一个线程都是用"同一把锁"
* 多条语句对共享数据的操作
* }
*
3.12 什么是同步方法?(非静态) 以及静态同步方法他们说锁对象是谁?
/**
* 什么是同步方法?
* 如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
* 默认的锁对象this(非静态)
*
* 如果是静态的同步方法,锁对象是 "当前类名.class"--跟"反射"有关系
* 反射:Java代码经历三个阶段
* "SOURCE" ,"CLASS"(反射中的核心) "RUNTIME"
*
*
*/
package com.qf.thread_02;
/**
* 资源类
*/
public class SellTicket implements Runnable{
private static int tickets = 100 ;
//创建一把锁
private Object obj = new Object() ;
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//模拟一直有票
while(true){
if(x% 2==0){
// synchronized (obj) {
//synchronized (this) {//当前类对象的地址值引用
synchronized (SellTicket.class) {//当前类名.class:获取到当前类的字节码文件对象(静态同步方法的锁)
if (tickets > 0) {
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}else {
//x变量不能被2整除
sell() ; //同步方法(非静态)
}
x++ ;
}
}
//同步方法:如果一个成员方法中进来就是一个synchronized(锁对象){}----同步代码块,将synchronized提到方法声明上
//同步方法的锁对象:是this---当前类对象的地址值引用!
// public synchronized void sell(){
public static synchronized void sell(){//静态是随着类的加载而加载 ,静态的同步方法的锁对象是"类名.class"
/*synchronized (obj){
if (tickets > 0) {
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}*/
if (tickets > 0) {
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
3.13 wait()/notify()并非Thread类中为什么Object的?
wait()线程等待--->底层域wait(long time),Java的本地方法(底层语言操作),使用monitor(监视器锁)来调用wait方法(本质就是synchronized([ˈsɪŋkrənaɪzd])同步锁)
notify()/notifyAll():唤醒单个线程以及唤醒所有线程,都是锁对象的方法,
而锁对象可以是任意Java类对象,不会定义在Thread类中,而是在Object类中定义的
3.14 什么死锁?如何解决死锁----"生成者消费者模式思想"
/**
* 死锁:
* 两个或者两个以上的线程,在执行的过程中,互相持有对方的锁,需要等待对方释放锁,导致出现了互相等待情况!
*
* 举例:
* 中国人和美国人吃饭
* 一双筷子 / 一把刀/一把叉
*
* 中国人:
* 一根筷子/一把刀
* 美国人:
* 一跟筷子/一把叉
* 解决死锁:多个线程必须使用统一资源对象! (引入生产者和消费者模式思想)
*
*/
/**
* 资源类
*/
public class DieLock implements Runnable {
private boolean flag ;
public DieLock(boolean flag){
this.flag = flag ;
}
@Override
public void run() {
if(flag){
synchronized (MyLock.objA){
System.out.println("if ObjA...");
synchronized (MyLock.objB){
System.out.println("if ObjB...");
}
}
}else {
synchronized (MyLock.objB){
System.out.println("else ObjB...");
synchronized (MyLock.objA){
System.out.println("else objA...");
}
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock d1 = new DieLock(true) ;
DieLock d2 = new DieLock(false) ;
//创建线程类
Thread t1 = new Thread(d1) ;
Thread t2 = new Thread(d2) ;
//启动线程
t1.start();
t2.start();
}
}
/**
* 第一种情况:
* t1线程先进来了,t1线程d1资源类对象--->执行true的结果,先持有了ObjA这个锁(if ObjA),需要执行"if ObjB",
* 等待t2线程去将objB这锁释放;t2线程再执行执行"else ObjB",--->需要等待t1线程释放ObjA这个锁"else ObjA"
* if ObjA...
* else ObjB...
*
* 第二种情况:
* else ObjB... t2线程先抢占到---等待t1线程释放锁
* if ObjA.. t1线程等待t2线程是否ObjB这个锁
*
*
* 第三种情况:理想状态:
* else ObjB... t2线程抢占到了, else ObjB---在执行下面else ObjeA,t1线程已经释放了ObjA这个锁
* else objA...
* if ObjA...
* if ObjB...
*/
3.15 jdk5提供了根据的锁操作---Lock(接口)和synchronized同义
/**
* jdk5以后提供了具体的"锁定操作"
* java.util.concurrent.locks.Lock接口---->可重入的互斥锁ReentrantLock([ri:'entrənt] )
* 提供一些方法,可以获取锁,可以去释放锁
* void lock() 获取锁
* void unlock()释放锁
*
* 电影院三个窗口卖票---使用Lock解决多线程安全问题----和synchronized是同样的语义
*/
//之前:锁对象可以是任意Java类对象,现在jdk提供Lock具体的锁----ReetrantLock子实现类
private Lock lock = new ReentrantLock() ;
try{
//获取锁
// void lock() 获取锁
// synchronized (自定义一个锁){}
lock.lock();
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}finally {
//finally除非一种特例:在执行finally语句之前,jvm退出 System.exit(0) ; ,任何情况下,finally语句一定会执行!
//使用完,释放锁
lock.unlock();
}
3.16 创建线程的另一种方式---->"线程池"
1.线程池:属于”池化技术“
经常用到:”创建固定的可重用的线程数的一个线程池“
可以重复利用的 Executors([ɪgˈzɛkjətərz])
/**
* 线程池:
* 可以通过Executors工厂类(当前类构造方法私有化,对外提供静态方法--->简单工厂模式)提供一些创建线程池的功能
* 可以创建固定的可重用的线程数的线程池
* public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
* 上面的返回值ExecutorService接口
* 提供一些方法
* Future<?> submit(Runnable task)提交异步任务
* <T> Future<T> submit(Callable<T> task):提交异步任务
* void shutdown():关闭线程池
*
*
*
//创建线程池---创建固定的可重用的线程数的线程池
// public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建两个线程池
//提交异步任务:展示两个线程并发执行的,不做计算,submit不需要返回值结果
// submit(Runnable task)提交异步任务
// MyRunnable my = new MyRunnable() ;
// threadPool.submit(my) ;
// threadPool.submit(my) ;
// <T> Future<T> submit(Callable<T> task):提交异步任务 ,不做异步任务的计算,不需要返回结果
MyCallable myCallable = new MyCallable() ;
threadPool.submit(myCallable) ;
threadPool.submit(myCallable) ;
// void shutdown():关闭线程池
threadPool.shutdown();
-------------------------------------------------------------------------
异步提交任务计算求和
import com.qf.executors_06.MyCallable;
import java.util.concurrent.Callable;
/**
* 异步任务
*/
public class MySelfCallable implements Callable<Integer> {
private int number ;
public MySelfCallable(int number){ //100或者200
this.number = number ;
}
@Override
public Integer call() throws Exception {
//定义一个最终结果变量
int sum = 0 ;
for(int x = 1; x <= number ;x++){
sum += x ;
}
return sum;
}
}
/**
* 需求:使用线程池的方式完成多个线程的异步计算:
* 1个线程求1-100的和
* 另一个线程求1-200的和
*
* 1)创建线程池:固定的可重用的线程数的线程池
* 2)提交异步任务<T> Future<T> submit(Callable<T> task):提交异步任务
* 返回值:Future接口:代表异步计算的结果
* V get():获取计算的结果
*/
public class Test {
public static void main(String[] args) throws
ExecutionException, InterruptedException {
//1)创建线程池:固定的可重用的线程数的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//2)提交异步任务
//<T> Future<T> submit(Callable<T> task):
Future<Integer> f1 = threadPool.submit(new MySelfCallable(100));
Future<Integer> f2 = threadPool.submit(new MySelfCallable(200));
System.out.println(f1.get());
System.out.println(f2.get());
//3)关闭线程池
threadPool.shutdown();
}
}
3.17 Callable和Runnable的区别
1、最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
2、callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出
4.单例模式
1.单例设计模式----属于创建型设计模式(创建对象)
概念:
始终在内存中有且仅有一个当前类的实例(有一个对象)
* 单例设计模式:
* 1)饿汉式
*
* 2)懒汉式
*
* 饿汉式:不会出现安全问题的单例设计模式
* 1)当前类是具体类
* 2)类一加载就创建当前类实例
* 3)构造私有化,对外隐藏,不能new实例
* 4)对外提供静态方法,返回值当前类本身
*
* Java中的类Runtime类:标准的单例(饿汉式),和计算机的运行环境有关系
*/
单例模式之懒汉式
1)当前类是个具体类
2)当前类构造方法私有化
3)当前类的成员位置:声明当前类的一个静态变量
4)对外提供静态方法,返回值是当前类本身:需要判断当前变量是否为null
现实可发业务中:
存在懒加载(按需加载)
一个用户有多个账户:一对多
4.1 I/O流
/**
* java.io.File--->表示文件或者文件夹(目录)一种抽象路径形式
* 构造方法:
* File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
* File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
* File(String parent, String child) 父路径名字符串和子路径名字符串创建新的 File实例
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
//描述:D盘下的demo文件夹中的a.txt文件
//File(String parent, String child)
File file = new File("D:\\demo","a.txt") ;
System.out.println(file);
System.out.println("----------------------------------------") ;
//File(File parent, String child)
File file2 = new File("D:\\demo") ;
File file3 = new File(file2,"a.txt") ;
System.out.println(file3);
System.out.println("----------------------------------------") ;
//File(String pathname)(推荐)
File file4 = new File("D:\\EE_2302\\day26\\code\\a.txt") ;
System.out.println(file4.createNewFile()) ;//创建文件
System.out.println(file4);
}
}
/**
* File的基本功能以及特有功能:
* public boolean createNewFile() throws IOException:创建文件
* public boolean mkdir():创建文件夹
* public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
* public boolean delete():删除文件夹或则文件路径,如果是文件夹,必须是空目录
* public boolean exists():判断此路径名表示的文件或者目录是否存在
* public boolean isDirectory():此抽象路径名表示的文件是否是目录
* public boolean isFile():是否是文件
* public boolean isHidden():是否隐藏
*
*
* 特有功能:高级功能
* public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
* public File[] listFiles():获取指定路径名表示的文件或者目录的File数组
*/
public class FileDemo2 {
public static void main(String[] args) throws IOException {
//在创建D:\EE_2302\day26\code\demo文件夹
//表示它的路径
File file = new File("D:\\EE_2302\\day26\\code\\demo") ;
//public boolean mkdir():创建文件夹
System.out.println(file.mkdir());
System.out.println("--------------------------------") ;
//public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
//创建aaa\bbb\ccc 多级目录(直接指定具体文件夹或者文件,相对路径创建)
File file2 = new File("aaa\\bbb\\ccc") ;
System.out.println(file2.mkdirs());
System.out.println("---------------------------------") ;
File file3 = new File("a.txt") ;
//创建文件
System.out.println(file3.createNewFile());
System.out.println("--------------------------------");
System.out.println(file3.exists());
System.out.println(file3.isDirectory());
System.out.println(file3.isFile());
System.out.println(file3.isHidden());
System.out.println(file3.canRead());
System.out.println(file3.canWrite());
System.out.println("-----------------------------------");
//public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
//public File[] listFiles():获取指定路径名表示的文件或者目录的File数组
//描述D盘---所有的文件夹以及文件的名称 获取到
File myFile = new File("d:\\") ;
String[] strs = myFile.list() ;
if(strs!=null){
for (String str : strs) {
System.out.println(str);
}
//System.out.println(Arrays.toString(strs));
}
}
}
/**
* 获取D盘下的所有的以".jpg"结尾的文件
* "需要将文件名打印到控制台上"
*
* 分析:
* 1)描述D盘---使用File对象
* 2)通过 public File[] listFiles() :获取指定路径下所有的文件以及文件夹的File数组
* 3)非空判断
* 3.1) 遍历File数组,获取到每一个File对象
* 3.2)如果当前File对象所表示文件路径的形式
* 3.2.1)是文件,判断文件是".jpg"结尾
* 3.2.2)输出这个文件的名称 public String getName()
*/
public class FileTest {
public static void main(String[] args) {
//1)描述D盘---使用File对象
File file = new File("D:\\") ;
//2)通过 public File[] listFiles() :获取指定路径下所有的文件以及文件夹的File数组
File[] files = file.listFiles();
if(files!=null){
for (File f : files) {
//f:代表File对象
//判断是否是文件
if(f.isFile()){
//满足是文件
//获取文件名称,以".jpg"结尾
if(f.getName().endsWith(".jpg")){
//输出文件名称
System.out.println(f.getName());
}
}
}
}
}
}
/**
* 获取D盘下的所有的以".jpg"结尾的文件
* "需要将文件名打印到控制台上"
*
* 之前的写法:
* 1)先获取当前指定路径形式下的所有的文件以及文件夹的File数组或者String[]数组
* 2)然后在遍历
* 另一种方式:直接获取的同时就将满足条件的内容遍历
* 参数都是接口---FilenameFilter:文件名称过滤器
* public boolean accept(File dir,String name)
* 将指定的文件存储在文件列表中dir,返回值true(存储在文件列表中),否则false,不存储
* public File[] listFiles(FilenameFilter filter)
* public String[] list(FilenameFilter filter)
*
*
*/
public class FileTest2 {
public static void main(String[] args) {
//1)描述D盘:使用File对象
File file = new File("D:\\") ;
//2)public String[] list(FilenameFilter filter) 直接获取满足条件的文件,存储在String数组中
String[] strs = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//同时满足条件:
//创建File对象--描述指定目录的文件列表
File f = new File(dir, name);
//分步完成
//f是表示文件形式
//boolean flag = f.isFile();
//f是文件,文件名称是以".jpg"结尾
//boolean flag2 = f.getName().endsWith(".jpg");
//return flag && flag2;
//一步走
return f.isFile() && f.getName().endsWith(".jpg") ;
}
});
if(strs!=null){
for (String str : strs) {
//满足文件列表的内容输出
System.out.println(str);
}
}
}
}
4.2 字节输出流
/**
* 字节输出流:
* OutputStream---抽象类--不能new,具体的子类:针对文件操作:FileOutputStream
*
* 操作步骤:
* 1)创建字节输出流对象
* 2)写数据
* 3)释放资源
*/
//public FileOutputStream(String name)throws FileNotFoundException
//OutputStream out = new FileOutputStream("fos.txt") ;//多态
FileOutputStream fos = new FileOutputStream("fos.txt") ;//具体类new --->这个过程:需要创建系统资源(C语言)
//写数据
//void write(byte[] b):写字节数组
//void write(byte[] b, int off, int len):写字节数组一部分
//abstract void write(int b) 写一个字节
//fos.write(97) ;//写一个字节
byte[] bytes = {98,99,100,101,102,103,104} ;
fos.write(bytes) ;
fos.write(bytes,0,3);
//释放资源:将流对象指向的文件地址的系统资源进行释放
fos.close() ;
//fos.write(108); 当流对象关闭,释放系统资源,不能在写数据的!否则,IOException: Stream Closed
/**
* 字节输出流:
* FileOutputStream实现文件的字节数追加
* public FileOutputStream(String name,
* boolean append)
* throws FileNotFoundException
* 第二个参数是true:自动追加内容
*
* IO流写数据,实现换行效果 widows系统 "\r\n"
*/
public class FileOutputStreamDemo2 {
public static void main(String[] args) throws IOException {
//创建FileOutputStream流对象
FileOutputStream fos2 = new FileOutputStream("fos2.txt",true) ;
//fos2.write("world".getBytes()) ;
for(int x = 0 ; x < 10 ; x ++){
fos2.write(("hello"+x).getBytes());
//写入换行符号
fos2.write("\r\n".getBytes());
}
//关闭资源
fos2.close();
4.3 字节输入流
/**
* InputStream:字节输入流--->抽象类--->具体的子类FileInputStream:文件字节输入流
* 1)创建文件字节输入流对象FileInputStream(String name) :读取指定的文件 name:文件地址
* 2)读数据
* 继承它父类的方法:public abstract int read()throws IOException:一次读取一个字节
* public int read(byte[] b) throws IOException:一次读取一个字节数组
*
*
* 需求:
* 使用字节输入流将当前项目下的"fis.txt"文件内容读取出来打印在控制台上
*
* 一次读取一个字节的方式:
* 针对中文---会出现乱码 (char)字节数,平台默认编码格式utf-8:一个中文对应三个字节
* 英文字母和中文-拼接一块,一个英文字母对应一个字节,这个时候拼接不上,导致文乱码---Java提供"字符流",解决乱码
*/
/** * 使用字节输入流一次读取一个字节数组的方式: * public int read(byte[] b) throws IOException:一次读取一个字节数组 */ public class FileInputStreamDemo2 { public static void main(String[] args) throws IOException { //创建一个字节输入流对象 // FileInputStream fis2 = new FileInputStream("fis2.txt") ; //读取当前项目下的FileOutputStreamDemo3.java FileInputStream fis2 = new FileInputStream("FileOutputStreamDemo3.java") ; //第一次读取 /* byte[] bytes = new byte[5] ; // public int read(byte[] b)--->返回:读取的字节数组的长度 int len = fis2.read(bytes) ; System.out.println(len); //将bytes字节数组---->String(byte[] bytes,int offset,int len) //每一次都是0开始读取实际字节数 System.out.println(new String(bytes,0,len)); System.out.println("--------------------------------------") ; //第二次读取 len = fis2.read(bytes) ; System.out.println(len) ; System.out.println(new String(bytes,0,len)); System.out.println("------------------------------------------"); //第三次读取 len = fis2.read(bytes) ; System.out.println(len) ; System.out.println(new String(bytes,0,len)); System.out.println("------------------------------------------"); //第四次读取 len = fis2.read(bytes) ; System.out.println(len) ; System.out.println(new String(bytes,0,len)); System.out.println("------------------------------------------"); //第五次读取 len = fis2.read(bytes) ; System.out.println(len) ;*/ //System.out.println(new String(bytes,0,len)); //最终代码:字节流一次读取一个字节数组 //创建一个字节缓冲区:长度:1024或者1024整数倍 byte[] bytes = new byte[1024] ; //实际字节数从0开始 int len = 0 ; while((len=fis2.read(bytes))!=-1){//赋值,判断 -- read(byte[] bytes):阻塞式方法,没有到-1,就一直读 String str = new String(bytes,0,len) ; System.out.println(str); } } }
/**
* 需求:
* 将当前项目下的myavi.mp4文件复制到D:\EE_2302\day26\code\copy.mp4
*
* 分析:
* 当前项目下的myavi.mp4---->源文件地址---->使用字节输入FileInputStream读取
* D:\EE_2302\day26\code\copy.mp4---目的地文件---->使用字节输出流FileOutputStream写
*/
public class CopyMp4 {
public static void main(String[] args) {
long start = System.currentTimeMillis() ;
//共耗时:222564毫秒
//copyMp4("myavi.mp4","D:\\EE_2302\\day26\\code\\copy.mp4");
//共耗时:356毫秒
copyMp4_2("myavi.mp4","D:\\EE_2302\\day26\\code\\copy.mp4");
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒");
}
/**
* 使用字节输入流一次读取一个字节数组
* @param srcFile 源文件
* @param destFile 目标文件
*/
public static void copyMp4_2(String srcFile,String destFile){
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
fis = new FileInputStream(srcFile) ;
fos = new FileOutputStream(destFile) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
//实际字节数
int len = 0 ;
while((len=fis.read(bytes))!=-1){
//一次读取一个字节数组,写字节数组(每次0开始,写入实际字节数)
fos.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 使用字节输入流一读取一个字节的方式,进行文件复制
* @param srcFile 源文件地址
* @param destFile 目标文件地址
*/
public static void copyMp4(String srcFile, String destFile) {
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
//创建字节输入流操作srcFile
fis = new FileInputStream(srcFile) ;
//创建字节输出流对象操作destFile
fos = new FileOutputStream(destFile) ;
//一次读取一个字节
int by = 0 ;
while((by=fis.read())!=-1){
//使用字节输入流一次一个字节,使用fos输出流对象写一个字节
fos.write(by) ; //不需要强转,要的字节
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.4 递归
方法递归:
* 方法本身调用方法的一种现象
* 前提条件:
* 1)定义一个方法
* 2)这个方法的逻辑是规律的
* 3)这个方法是有结束条件,否则---成了"死递归"
*
* 注意:
* 构造没有递归
*
* 需求:
* 求5的阶乘
* 1)可以使用循环解决---求阶乘思想
* 5!= 5*4!
* --->
* --->
*
* 2)使用递归:
* 1)定义一个方法
* 2)有规律:
* 分解法:
* 将问题1
* 拆分问题11
* 5!=5*4!
* 4! = 4 * 3!
* 3! = 3 * 2!
* 2! = 2 * 1!
* 1!永远是1
public class DiGuiDemo { public static void main(String[] args) { //求5的阶乘 //定义最终结果变量 int jc = 1 ; for(int x = 1 ; x <= 5 ; x ++){ jc *= x ; } System.out.println("5的阶乘是:"+jc); System.out.println("----------------------------------------------") ; System.out.println("5的阶乘是:"+getJc(5)); } public static int getJc(int n){ //5 if(n==1){ return 1 ; }else { //5*getJc(4) //4*getJc(3) //3*getJc(2) //2*getJc(1) return n * getJc(n-1) ; } }
4.5 字节缓冲流
/**
* Java之IO流提供了字节缓冲流:让读取的速度更快一些,提供对应的类
* 字节缓冲输入流/字节缓冲输出流 (高效的字节流)---仅仅是内部提供缓冲区字节数组长度:8192长度,
* 文件的读写复制还的依赖于基本字节流(InputStream/OutputStream)
* BufferedInputStream(InputStream in):字节缓冲输入流
* BufferedOutputStream(OutputStream out):字节缓冲输出流
*
* 使用字节缓冲流来完成读写复制---看他们执行效率
*
* 使用基本字节流一次读取一个字节:共耗时:222564毫秒
* 使用基本字节流一次读取一个字节数组:共耗时:356毫秒
*
* 推荐"使用字节缓冲流"完成图片文件/视频文件/音频文件的读取!
*
* 使用字节缓冲流:一次读取一个字节: 共耗时:1250毫秒
* 使用字节缓冲流:一次读取一个字节数组: 共耗时:110毫秒
* 需求: * 将当前项目下的myavi.mp4--->复制到 D:\EE_2302\day27\code\copy.mp4 * * */ public class CopyFileTest { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); // copyFile("myavi.mp4","D:\\EE_2302\\day27\\code\\copy.mp4") ; copyFile2("myavi.mp4","D:\\EE_2302\\day27\\code\\copy.mp4") ; long end = System.currentTimeMillis() ; System.out.println("共耗时:"+(end-start)+"毫秒"); } /** * 使用字节缓冲输入流一次读取一个字节数组 * @param srcFile 源文件地址 * @param destFile 目标文件地址 */ public static void copyFile2(String srcFile, String destFile) throws IOException { //BufferedInputStream(InputStream in):字节缓冲输入流 //创建字节缓冲输入流对象 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile)) ; // BufferedOutputStream(OutputStream out):字节缓冲输出流 //创建字节缓冲输出流对象 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile)) ; //一次读取一个字节数组 byte[] bytes = new byte[1024] ; int len = 0 ;//实际字节数 while((len=bis.read(bytes))!=-1){ //每次从0开始写入实际字节数 bos.write(bytes,0,len); //使用字节缓冲输出流的时候,防止一些字节数没有交给OutputStream字节输出流 //强制刷新缓冲输出流(尤其图片,图片文件带有缓冲数据:可能字节数没有写入到底层输出流中,导致图片文件缺失) bos.flush() ; } //释放资源 bos.close(); bis.close(); } /** * 使用字节缓冲输入流一次读取一个字节 * @param srcFile 源文件地址 * @param destFile 目标文件地址 */ public static void copyFile(String srcFile, String destFile) throws IOException { //BufferedInputStream(InputStream in):字节缓冲输入流 //创建字节缓冲输入流对象 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile)) ; // BufferedOutputStream(OutputStream out):字节缓冲输出流 //创建字节缓冲输出流对象 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile)) ; //一次读取一个字节 int by = 0 ; while((by=bis.read())!=-1){ //读取一个字节,写出一个字节 bos.write(by) ; } //释放资源 bos.close(); bis.close(); } }
4.6 字符输入输出流
/** * 字符输入流---->Reader(抽象类)---> * 具体的子类:字符转换输入流InputStreamReader --- 将字节输入流---->转换--->字符输入流 * public InputStreamReader(InputStream in):使用平台默认字符集进行解码---读取数据 * public InputStreamReader(InputStream in,String charsetName) * 使用指定字符集解码---读取数据 * * 读数据: * int read() :一次读取一个字符 --->返回结果:实际的字符数 * int read(char[] cbuf) :一次读取一个字符数组 * int read(char[] cbuf,int off, int len):读取字符数组的一部分 * */
public class InputStreamReaderDemo { public static void main(String[] args) throws Exception { //创建字符转换输入流对象 //解码:gbk(中国编码表): 一个中文对应两个字节 /* InputStreamReader isr = new InputStreamReader( new FileInputStream("osw.txt"),"gbk") ;*/ //public InputStreamReader(InputStream in) InputStreamReader isr = new InputStreamReader( new FileInputStream("osw.txt")) ; //读数据 //一次读取一个字符 int by = 0 ;//实际字符数 while((by=isr.read())!=-1){ //展示控制台上 读取的时候将文件内容的里面--->int类型----->转换成字符 System.out.print((char)by); } } }
/** * 字符流: * 字符输出流:Writer(抽象类)---> * 具体的子类 OutputStreamWriter:字符转换输出流,"可以将字节输出流---转换---字符输出流" * 构造方法: * public OutputStreamWriter(OutputStream out):使用平台默认字符集进行编码--输出数据 * public OutputStreamWriter(OutputStream out,String charsetName): * 使用指定的字符集进行编码---输出数据 * 写数据: * void write(char[] cbuf) :写入字符数组 * 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 Exception { //创建一个字符转换输出流对象 //utf-8:一个中文对应三个字节 /* OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("osw.txt"),"utf-8") ;*/ OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("osw.txt")); //写字符串 osw.write("helloworld"); osw.write("高圆圆"); osw.write(97) ;//写入字符,传入参数int--->ASCII码表对应的字符值 //释放资源 osw.close(); } }
4.7 字符缓冲流
/** * BufferedReader:字符缓冲输入流---可以读取一行内容 * 构造方法: * public BufferedReader(Reader in):提供默认缓冲区大小的字符缓冲输入流 (8192个长度) * 这个流如果直接操作文件--->参数里面使用FileReader * public String readLine()throws IOException * * 特有功能: * public String readLine()throws IOException:一次读取一行 * 返回值是读取到这一行的内容,当流已经到达末尾,则返回null * * 键盘录入 * 1)Scanner(InputStream in)---->Scanner sc = new Scanner(System.in) ; * 2)new BufferedReader(new InputStreamReader(System.in)) * --->InputStreamReader(InputStream in) */
public class BufferedReaderDemo { public static void main(String[] args) throws IOException { //创建一个字符缓冲输入流对象 //BufferedReader br = new BufferedReader(new FileReader("br.txt")) ; BufferedReader br = new BufferedReader(new FileReader("copy.java")) ; //public String readLine()throws IOException:一次读取一行 // 返回值是读取到这一行的内容,当流已经到达末尾,则返回null /*//第一次读取 String line = br.readLine(); System.out.println(line); //第二次读取 line = br.readLine() ; System.out.println(line); //第三次读取 line = br.readLine() ; System.out.println(line); //第四次读取 line = br.readLine() ; System.out.println(line); //第五次读取 line = br.readLine() ; System.out.println(line);*/ //最总版本:循环改进 String line = null ; while((line=br.readLine())!=null){ //赋值,判断 readLine() --->阻塞式方法 System.out.println(line); } //释放资源 br.close(); } }
/**
* 字符缓冲输出流:提供字符数组/字符/字符串的一种高效写入
* BufferedWriter构造方法
* public BufferedWriter(Writer out):提供默认缓冲区大小的字符缓冲输出流,默认缓冲区足够大 8192长度
* 写的功能:
* write(字符/字符数组/字符串...)
* 特有功能:
* 不需要在使用"\r\n"换行符号, public void newLine()写入行的分隔符
*/
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
//创建字符缓冲输出流对象
// BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ;
//FileWriter(Stirng pathname,boolean append):第二个参数true,自动追加(字符追加到末尾)
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt",true)) ;
//写数据
for(int x = 1 ; x <=10 ;x ++){
bw.write("hello"+x);
//换行
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
}
}
4.7 URL-->统一资源定位符号
/**
* 需求:
* 需要将网络图片下载到本地
* Java提供了类:URL-->统一资源定位符号
* 1)创建URL对象public URL(String spec)
* 2)使用URL建立http请求---建立连接
* public URLConnection openConnection()throws IOException
* URLConnnection:和http建立的通信协议(不能new)
*
* HttpURLConnection--子类提供一些方法:
* public InputStream getInputStream()throws IOException 获取到字节输入流
* 3)创建基本字节输出流FileOutputStream
* 4)一次读取一个字节数组的方式,将图片复制到本地磁盘上
*
*/
public class Test2 {
public static void main(String[] args) {
HttpURLConnection httpURLConnection = null ;
InputStream inputStream = null ;
FileOutputStream fos = null ;
try {
//1)创建URL地址对象
URL url = new URL("http://rs0k1dy20.hd-bkt.clouddn.com/bug.jpg");
//2)建立连接通信
//public URLConnection openConnection()throws IOException
// URLConnnection:和http建立的通信协议(不能new)
httpURLConnection = (HttpURLConnection) url.openConnection();
//3)获取输入流对象-读取网络地址的内容
inputStream = httpURLConnection.getInputStream();
//4)创建字节输出流对象
fos = new FileOutputStream("gaoyuanyuan.jpg") ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=inputStream.read(bytes))!=-1){
//写
fos.write(bytes,0,len) ;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(httpURLConnection!=null){
//让连接失效public abstract void disconnect()
httpURLConnection.disconnect();
}
}
}
}
4.8 Properties
/**
* java.util.Properties:属性集合列表---extends Hashtabl<K,V>-->
*
* 本质是Map<K,V>集合
* 添加功能:put(K key,V value)
* 遍历方式:Set<K> keySet()
* 遍历键-->通过键获取值 V get(K key)
*
* 自己的特有功能:
* java.util.Properties:没有泛型,键和值都只能String---->
* 作用:用来读取配置文件xx .properties配置文件(src下面)
* 构造方法:
* public Properties()
* 添加属性列表的键和值:
* public Object setProperty(String key,String value)
* 特有的遍历方式:
* public Set<String> stringPropertyNames():获取属性列表中的所有的键(属性名称)
* public String getProperty(String key):通过属性列表中的属性名称获取对应的值
public class PropertiesDemo {
public static void main(String[] args) {
//创建属性集合列表
Properties prop = new Properties() ;
System.out.println(prop);
//public Object setProperty(String key,String value)
prop.setProperty("张三","30") ;
prop.setProperty("李四","25") ;
prop.setProperty("王五","35") ;
System.out.println(prop);
//遍历
// public Set<String> stringPropertyNames():获取属性列表中的所有的键(属性名称)
// public String getProperty(String key):通过属性列表中的属性名称获取对应的值
Set<String> keySet = prop.stringPropertyNames();
for(String key:keySet){
String value = prop.getProperty(key);
System.out.println(key+"---"+value);
}
}
}
/**
* java.util.Properties可以保存到流中或从流中加载
*
* 将文件的内容加载到属性集合列表中:
* public void load(InputStream inStream/Reader)
*
* 将属性列表中的内容保存到指定文件中
* 第二个参数:描述属性列表
* public void store(OutputStream out/Writer,String comments)
*
* 举例:
* 游戏的关卡的加载--->读文件
* 游戏津达的保存----->写文件
public class PropertiesDemo2 {
public static void main(String[] args) throws IOException {
// myLoad();
myStore() ;
}
//写
private static void myStore() throws IOException {
//将属性列表中的内容---保存指定文件中
Properties prop = new Properties() ;
//存储用户名和密码
prop.setProperty("文章","123") ;
prop.setProperty("高圆圆","123456") ;
prop.setProperty("admin","admin") ;
System.out.println(prop) ;
//public void store(OutputStream out/Writer,String comments)
prop.store(new FileWriter("D:\\EE_2302\\day27\\code\\day27\\src\\my.properties"),"name'list") ;
}
private static void myLoad() throws IOException {
//创建属性集合列表
Properties prop = new Properties() ;
System.out.println(prop);
//如何读取src下面的xx.properties文件
//1)获取当前类的字节码文件对象--->类名.class 属性
// Class c = PropertiesDemo2.class ;
//2)通过获取到的字节码文件对象获取 当前类的加载器
// public ClassLoader getClassLoader()
// ClassLoader classLoader = c.getClassLoader();
//3)ClassLoader---类加载器--->读取到src下面配置文件的内容--->存储到字节输入流中
//public InputStream getResourceAsStream(String name) 返回用于读取指定资源的输入流
//InputStream inputStream = classLoader.getResourceAsStream("name.properties");
//一步走
InputStream inputStream = PropertiesDemo2.class.getClassLoader().
getResourceAsStream("name.properties");
//public void load(InputStream inStream/Reader)
prop.load(inputStream);
System.out.println(prop);
}
}
5. 网络编程
1.网络三要素
ip地址:
端口号
协议
ip地址:A/B/C
A类---->前面第一个号段:网络号段 后面的号段:主机号段(国家部门---机房)
B类----->前面两个号段:网络号段 后面的号段:主机号段(大学校园/公司内网等)
C类----->属于私人地址:前面三个号段:网络号段 后面的号段:主机号段(私人)
点分十进制法--->10.35.165.17
端口号:0-65535 这里面0-1024(属于保留端口号)
自己指定端口号----->1025---65535(不要和系统端口号冲突----Adress already in use:BindException:地址被占用" )
mysql端口号:3306
tomcat:web服务器:8080
协议:
TCP/UDP协议(底层网络协议)
UDP协议:
1)属于不可靠连接,不需要建立连接通道(不安全)----以“数据报包”的方式进行数据传输的
2)不同步的,执行效率相对于TCP协议,效率高
3)发送数据大小由限制的!
TCP协议:
1)属于可靠连接,需要建立连接通道!----以“流”的方式发送给数据
2)同步的,执行效率低,安全性相对UDP协议(安全性能高)
3)发送数据大小无限制!
网络编程--->“Socket”编程
发送端/客户端
接收端/服务器端
两端都必须有Socket对象!("套接字")
/**
* java.net.InetAddress:代表互联网ip地址对象!
* 获取ip地址对象---->获取ip地址字符串形式/获取主机名称
* 参数:要么传入主机名称/要么ip地址字符串
* public static InetAddress getByName(String host)throws UnknownHostException :获取互联ip地址对象
*
* InetAddress的成员方法
* public String getHostAddress():获取ip地址字符串形式
* public String getHostName():获取ip地址的主机名
/**
* 使用UDP协议发送数据
* 1)创建发送端的socket对象
* 2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
* 3)使用发送端的Socket对象发送"数据报包"
* 4)释放资源
*/
//1)创建发送端的socket对象 ---java.net.DatagramSocket:此类表示用于发送和接收数据报数据包的套接字
//public DatagramSocket()throws SocketException
DatagramSocket ds = new DatagramSocket() ;
//2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
//java.net DatagramPacket
//参数1:分组数据(字节数组)
//参数2:包的长度
//参数3:ip地址对象
//参数4:端口号
//public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
byte[] bytes = "hello,udp我来了".getBytes() ;
int length = bytes.length ;
InetAddress inetAddress = InetAddress.getByName("10.35.165.17") ;
int port = 6666 ;
DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;
//使用发送端的Socket对象发送"数据报包"
//public void send(DatagramPacket p)throws IOException从此套接字发送数据报包
ds.send(dp) ;
//释放资源
ds.close();
}
}
/**
/**
* Udp接收端
* 1)创建接收端的Socket对象
* 2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
* 3)使用2)接收容器(缓冲区),接收发送端的数据
* 4)解析接收容器中的实际内容
* 5)展示数据即可
* 6)释放接收端的资源
*
* 注意:
* 接收端开启一次就可以;否则
* Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
*/
public class UdpReceive {
public static void main(String[] args) throws Exception {
//1)创建接收端的Socket对象
//public DatagramSocket(int port)throws SocketException
DatagramSocket ds = new DatagramSocket(6666) ;
//2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
//参数1:用于保存传入"数据报"的缓冲区。
//参数2:要读取的字节数
//public DatagramPacket(byte[] buf,int length)
byte[] buffer = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
//3)使用2)接收容器(缓冲区),接收发送端的数据
//public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包
ds.receive(dp) ;
//4)解析接收容器中的实际内容
//public byte[] getData() :解析数据报包中DatagramPacket缓冲的实际的字节数组
byte[] bytes = dp.getData();
//public int getLength():解析数据报包中DatagramPacket中的实际字节数组长度
int length = dp.getLength();
//展示数据
String message = new String(bytes,0,length) ;
//谁发送的数据---获取ip地址字符串形式
//数据报包DatagramPacket--->public InetAddress getAddress()
//InetAddress:ip地址--->String getHostAddress()
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from is--->"+ip+",data is--->"+message) ;
//释放资源
ds.close();
}
}