JavaSE基础(W5)总结
Day1
增强for循环(JDK5)以后出现
使用场景:
遍历集合或数组,遍历集合居多,增强for`循环是为了简化迭代器的书写方式,替代集合的迭代器 集合中使用较为广泛
格式:
//for(集合中存储的数据类型 变量名 : 集合对象){
for(Student s : stuList){
//使用变量名
System.out.println(s.getName()+"---"+s.getAge());
//}
注意事项:使用增强for循环,前提条件是集合不能为空,否则空指针异常.
List集合的获取功能
ListIterator<E> listIterator() :获取列表迭代器
ListIterator
正向遍历
boolean hasNext()判断是否有下一个可迭代的元素
E next() 获取下一个元素
逆向遍历
boolean hasPrevious()判断是否有下一个可迭代的元素
E previous()获取上一个元素
得现有正向遍历才能逆向遍历,否则输出没有结果
无参构造创建的时候默认构造一个初始容量为10的空列表,当传递容量大小(有参构造方法)会自动扩容grow方法: 1.5倍的机制来进行扩容
线程安全的类
StringBuffer:字符缓冲区
Vector集合(其中的元素称之为"组件")
特有功能:
public void addElement(Object obj)添加元素
public Enumeration<E> elements():获取向量组件的枚举 (类似于 Iterator iterator())
Enumeration:
boolean hasMoreElements()判断是否有很多元素 类似于:boolean hasNext()
Object nextElement(): 类似于:Object next()
public E elementAt(int index):-------类似于:List集合中的get(int index)
获取索引值对应的"组件"
和size()相结合----普通for循环的遍历方式
LinkedList特有功能:
public void addFirst(E e):将元素添加到开头
public void addLast(E e):将元素添加到链表的末尾
public Object removeFirst():删除列表的第一个元素,并返回的是第一个元素
public Object removeLast():
public E getFirst():获取列表的开头元素
public E getLast()
Set集合与List的区别
List有序(存储和取出一致)
允许元素重复
Set无序(存储和取出不一致)
不允许元素重复
Set接口不能实例化,最具体的子类:使用HashSet以及TreeSet
HashSet(线程不安全的类:它允许null元素可以出现,执行效率高)----底层由HashMap实现
HashSet的add方法---依赖于HashMap的put方法
因为中文比较特殊,有哈希码值一样的情况,所以要重写哈希码值+equals
依赖于:hash方法需要计算每一个元素的hash码值(hashCode())
依赖equals方法:比较内容是否相同
当使用set集合存储自定义对象,要保证元素唯一,
就需要set集合底层依赖于hashCode和equals方法!
Set集合特点:它不保证遍历顺序恒久不变(哈希表决定的)
TreeSet集合:
基于TreeMap的实现(红黑树结构:自平衡的二叉树结构)
自然排序 public TreeSet()
比较器排序 public TreeSet(Comparator<? super E> comparator)
取决于使用的构造方法!
TreeSet集合:存储元素后,按照某种规则输出,规则提示明确主要条件,自己判断次要条件
TreeSet的add方法基于TreeMap的put方法有关系
特点:将第一个元素(节点)成为"root"根节点,后面的元素依次比较(小了作为左孩子,大了作为右孩子),重复不会添加(不会进入节点)
TreeSet存储自定义对象有两种排序:
自然排序:自定义对象所在类必须实现一个接口:Comparable,重写接口中的compareTo(T t)
比较器排序
方式1:自定义一个类实现Comparator接口,重写接口中的compare(T o1,T o2)(T泛型)
方式2:直接使用Comparator接口匿名内部类
自然排序
自然排序注意:存储自定义对象所在的了欸必须实现一个接口,Comparable接口,必须要给定一个排序的主要条件
重写compareTo()方法,否则报错:ClassCastException
自然排序:执行无参构造方法TreeSet<E>()
@Override//重写compareTo()方法
public int compareTo(Student s) {
//主要条件:按照学生的姓名长度从大到小
int num = s.name.length() - this.name.length() ;
//次要条件:如果姓名长度相同,比较姓名的内容是否一样
int num2 = (num==0)?this.name.compareTo(s.name):num ;
//如果姓名长度相同,姓名内容也一样,比较年龄是否一样
int num3 = (num2==0)? this.age-s.age : num2 ;
return num3 ;
}
比较器排序
方式1):利用comparator比较器函数接口的子实现类
public class MyCoparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//按照年龄从小到大排序
int num = s1.getAge() - s2.getAge() ;
int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
return num2;
}
}
方式2)使用匿名内部类(书写简单,而且节省空间)
import java.util.Comparator;
import java.util.TreeSet;
public class TreeDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>(){
@Override
public int compare(Student s1, Student s2) {
//按照年龄从小到大排序
int num = s2.getAge() - s1.getAge() ;
int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
return num2;
}
}) ;
Day21
Map集合和Collection集合的区别
Collection:单例集合:只能存储一种类型(引用类型)
Set集合和Map集合有一定的间接关系
HashSet<E>的添加功能add依赖Map里面的HashMap<K,V>的put方法
TreeSet<E>的add方法依赖于Map里面TreeMap<K,V>的put方法
Map:双列集合,可以存储键和值:两种类型
存储的一系列的键值对元素 Map集合:针对键有效,跟值无关,键必须唯一的!
默认的使用HashMap:允许元素出现null键和null值,线程不安全的类!
Map集合的功能:
添加
V put(K key,V value) :添加一个键值对元
注意事项: 细节:返回什么意思?
如果键是第一次添加,那么返回是null
如果键不是第一次添加,后面添加键对应的值将第一次的值覆盖掉,将第一次的值返回!
删除
void clear()
V remove(Object key):删除指定的键,返回被删除键对应的值
判断
boolean containsKey(Object key):判断是否包含指定的键
boolean containsValue(Object value):判断是否包含指定的值
获取
Collection<V> values():获取Map集合中的所有的值的集合
方式1:Map的遍历
Set<K> keySet():获取Map集合的中的所有的键
V get(Object key):然后在通过键获取值
//方式2:Map的遍历
Set<Map.Entry<K,V>> entrySet():获取键值对对象
HashMap:底层数据结构:哈希表:键必须唯一!值可以重复,存储和取出无序
Map的遍历
方式1
HashMap<String,String> hm = new HashMap<String,String>() ;
hm.put("杨过","小龙女") ; //添加元素
hm.put("郭靖","黄蓉") ;
hm.put("陈玄风","梅超风") ;
hm.put("令狐冲","任盈盈") ;
Set<String> keySet = hm.keySet(); //遍历:
for(String key :keySet){
//在键获取值
//V get(Object key)
String value = hm.get(key);
System.out.println(key+"="+value);
}
方式2
HashMap<String,String> hm = new HashMap<String,String>() ;
hm.put("杨过","小龙女") ; //添加元素
hm.put("郭靖","黄蓉") ;
hm.put("陈玄风","梅超风") ;
hm.put("令狐冲","任盈盈") ;
Set<Map.Entry<String, String>> entrySet = hm.entrySet();
for(Map.Entry<String,String> entry:entrySet ){
//分别获取键和值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
Map的键值
键:自定义类型:如何保证key是唯一的呢?
put方法----依赖于hashCode和equals方法,存自定义对象所在类必须重写!
HashMap<String,String>:Key:String String类型底层重写hashCode和equals方法!
针对集合操作的工具类
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 <T> int binarySearch(List<<? extends Comparable<? super T>>, T key)
集合中的二分搜索 :前提条件:当前集合中的元素必须有序的!
public static <T> void sort(List<T> list,Comparator<? super T> c):对列表元素进行随机置换
public static T max(Collection<? extends T> coll) :求最大值
public static T min(Collection<? extends T> coll):求最小值
//创建List集合对象
List<Integer> list = new ArrayList<Integer>() ;
list.add(20) ;
list.add(17) ;
list.add(19) ;
list.add(23) ;
list.add(21) ;
list.add(16) ;
list.add(24) ;
System.out.println(list);
Collections.sort(list);
System.out.println(list);
//public static <T> int binarySearch(List<<? extends Comparable<? super T>>, T key)
int index = Collections.binarySearch(list,20) ;
System.out.println(index);//排序后查找元素20的角标
System.out.println("-------------------------------------");
Collections.shuffle(list); //随机置换
System.out.println(list);
System.out.println(Collections.max(list));//求最大值
System.out.println(Collections.min(list));//求最小值
进程
进程:(程序的实体)由系统资源分配和调度的最小单元,而线程依赖于进程存在,程序执行的最小单位!
线程:程序执行时期的最小单元,所有线程都存在线程组(ThreadGroup)
Java语言不能够创建系统资源, 借助于底层语言C语言来操作,Java提供类Thread类
并发:在一个时间点同时发生
并行:在一个时间段内同时发生
开发步骤:
1)自定义一个类 继承自 Thread(线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。 )
2)该子类应重写 Thread 类的 run 方法(重写的run内部存放的耗时的代码:循环,io流读写操作...)
3)创建当前子类对象,然后启动线程:start()而非run方法()
启动线程:使用start 方法
成员方法:
设置线程名称
public final void setName(String name)
获取线程名称:
public final String getName()
重写run方法出现的异常:start()方法只能重写一次,否则IllegalThreadStateException:非法状态异常
public static void yield():暂停当前正在执行的线程,执行其他线程!
throws InterruptedException :等待该线程终止
public final void join()
throws InterruptedException :等待该线程终止
//try...catch...捕获异常
//throws:抛出异常:方法声明上
try {
tj1.join(); //等待tj1终止
} catch (InterruptedException e) {
e.printStackTrace();//日志: 跟踪堆栈,将异常信息打印在控制台上
}
Thread类的常量字段表
Thread类的常量字段表
public static final int MAX_PRIORITY 10
public static final int MIN_PRIORITY 1
public static final int NORM_PRIORITY 5 :默认值
public final void setPriority(int newPriority)设置优先级
public final int getPriority()
优先级越大的线程,抢占CPU的执行权(cpu一点点时间片,可以高效的进行切换)的资格大
优先级越小的线程:抢占CPU的执行权越小!
线程的执行具有随机性!
Day3
线程的六种状态
state{
NEW 新建状态
RUNNABLE 运行状态
BLOCKED 阻塞状态,通过sleep(),wait()
WAITING 等待,死死等待
TIMED_WAITING, 超时等待状态
TERMINATED; 线程死亡状态
}
多线程的实现方式Runnable
Java语言不能开启多线程,通过Thread实现Runnable接口,成为thread的子类,并且重写run方法
实现关系(Runnable)
步骤:
1)自定义一个类,实现Runnable接口,重写run方法;
2)在用户线程(main)创建"资源类"对象,然后创建Thread类对象,将"资源类"作为对象传递
3)分别启动线程
//Thread类的静态功能:
public static Thread currentThread():表示正在执行的线程对象的引用
案例1)使用继承关系
//创建三个线程
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();
-----------------
public class SellTicket extends Thread{
//有100张票
public static int tickets = 100 ;
//创建一个把锁
private Object obj = new Object() ;
@Override
public void run() {
//模拟一直有票
while(true){
synchronized (obj){
//加入网络延迟,睡眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets>0){
System.out.println(getName()+"正在出售第"+tickets+"张票");
tickets -- ;
}else{
break ;
}
}
}
}
}
方式2,使用Runnable接口
//创建资源共享类对象
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();
public class SellTicket implements Runnable {
public static int tickets = 100 ;//100张票
//t1,t2,t3
@Override
public void run() {
//为了模拟一直有票,使用死循环
while(true){
if(tickets>0){ //t1进来
//睡眠100毫秒
//为了模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"正在第"+(tickets--)+"张票");
}
//-------------------------
程序出现:
1)一个张票可能被卖多次(出现同票)
线程的执行具有随机性(原子性操作:最简单,最基本的操作语句:--,++...)
2)出现了负票
线程的延迟性导致(加入睡眠100毫秒)
现在的程序存在 "多线程安全问题"
//----------------------
校验多线程安全问题的标准
1)查看当前程序是否是多线程环境
2)是否存在共享数据
3)是否有多条语句对共享数据进行操作
关键字synchronized
synchronized:关键字---- 内置语言实现的(jvm来实现锁定操作: 锁的释放:自动释放)
可以是通过代码块(代码中),可以在方法上使用(同步方法)
synchronized同步锁---属于"悲观锁"
悲观锁:自己线程本身在执行的时候,其他线程不能进入同步代码块中,其他线程不能进入
修改数据,保证数据的安全性! (针对频繁的写入操作)
乐观锁:针对频繁读取数据的锁
程序添加synchronized同步锁
//----------------------------------------
public class SellTicket implements Runnable {
public static int tickets = 100 ;//100张票
private Demo demo = new Demo();
// private Object obj = new Object() ;
//t1,t2,t3
@Override
public void run() {
//为了模拟一直有票
while(true){
// synchronized(obj){ //一个锁对象
//锁对象可以是任意的Java类对象
synchronized(demo){ //一个锁对象
if(tickets>0){ //t1进来,t2和t3无法进入
try {
Thread.sleep(100); //睡眠100毫秒,模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"正在第"+(tickets--)+"张票");
}
}
}
}
}
class Demo{//自定义类
}
代理模式
代理:让别人替自己本身完成一些事情!
代理角色:帮助真实角色对他本身的功能进行增强(完成代理完成不了的事情!)
真实角色:只专注于自己完成的事情
Java代理模式----结构型设计模式
静态代理
同一个接口
特点:代理角色和真实角色必须同一个接口
动态代理(后期框架底层使用的这个模式)
JDK动态代理:基于接口
CGLib动态代理:基于子类
public class StaticProxyDemo {
public static void main(String[] args) {
//接口多态
//真实角色
Marry marry = new You() ;
marry.marry() ;
System.out.println("-----------------------");
//加入婚庆代理角色
You you = new You() ;
//创建婚庆公司
WeddingCompany wdc = new WeddingCompany(you) ;
wdc.marry();
}
}
//定义接口:Marry
interface Marry{
public abstract void marry() ;
}
//定义一个子实现类:真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("我很高兴,我要结婚了...");
}
}
//代理角色weddingCompany:婚庆公司 要实现Marry接口
class WeddingCompany implements Marry{
private You you ;
public WeddingCompany(You you){
this.you = you ;
}
@Override
public void marry() {
System.out.println("结婚之前,婚庆公司布置婚礼现场...");
you.marry();
System.out.println("结婚完毕,很高兴,给婚庆付尾款...");
}
}
//创建资源共享类对象
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();
//-----------------------------
public class SellTicket implements Runnable {
//100张票
public static int tickets = 100 ;
// public Object obj = new Object() ;
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//模拟一直有票
while(true){
if(x % 2 == 0){
// synchronized (this){
synchronized (SellTicket.class){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}else{
//x%2!=0
sellTicket();
}
x ++ ;
}
}
静态的同步方法 ---静态的东西都和类直接相关 :锁对象---就是 (反射相关)类名.class--->class 包名.类名{} 字节码文件对象
private static synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
锁
JDK5以后Java提供了比syncrhonized(jvm来操作:自动释放锁)更广泛的锁定操作,程序员可以在某个位置自己去加锁,(弊端):手动释放锁
java.util.concurrent.locks.Lock 接口 不能实例化
提供成员方法
void lock() :获取锁
void unlock():手动试图释放锁
ReentrantLock :子实现类 跟synchronized用于法相似
可重入的互斥锁!
synchronized: 关键字---> 内置语言实现---通过jvm调用的
由jvm自动释放锁
它不仅仅可以应用在方法体中(同步代码块),也可以引用在方法上(同步方法)
Lock :是一个接口
手动加锁和手动释放锁
一般都是在方法中的语句中使用
----------------------
//创建资源类对象
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();
public class SellTicket implements Runnable {
public static int tickets = 100 ;//定义100张票
private Lock lock = new ReentrantLock() ; //声明一个锁:Lock
@Override
public void run() {
while(true){ //死循环模拟一直有票
//获取一个锁:某个线程执行到这块:必须持有锁
lock.lock();
//执行完毕,手动释放锁
//在开发中 :try...catch...finally :捕获异常 ,不会使用throws
//变形格式:try...catch...catch...
//try...finally...
try{
if(tickets >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}finally {
//释放资源代码块
//释放锁
lock.unlock();
}
}
}
}
死锁
虽然syncrhonized可以解决线程安全问题:同步代码块/同步方法,但是执行效率低,可能出现死锁
两个线程或者多个线程出现互相等待的情况!
解决死锁问题方案:"生产者消费者思想" 必须保证多个多线程必须使用的同一个资源对象!
public class DieLockDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock dl1 = new DieLock(true) ;
DieLock dl2 = new DieLock(false) ;
//创建线程类对象
Thread t1 = new Thread(dl1) ;
Thread t2 = new Thread(dl2) ;
//启动线程
t1.start();
t2.start();
}
}
------------------------
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 ;
}
@Override
public void run() {
if(flag){
synchronized (MyDieLock.objA){
System.out.println("if objA");
synchronized (MyDieLock.objB){
System.out.println("if objB");
}
}
}else{
synchronized (MyDieLock.objB){
System.out.println("else objB");
synchronized (MyDieLock.objA){
System.out.println("else objA");
}
}
}
}
}
Day4
等待唤醒机制(学生数据私有,提供同步方法)
public class Student {
private String name ;
private int age ;
private boolean flag ;
//定义一个功能:给学生赋值(产生数据)
public synchronized void set(String name,int age){ //同步方法 锁对象:this
if(this.flag){//如果生产者有数据,等待消费者使用数据
try {
this.wait(); //立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name ;
this.age = age ;
//如果存在数据
this.flag = true ;
//唤醒阻塞中的线程:消费者线程
this.notify();
}
public synchronized void get(){ //获取学生的方法
if(!this.flag){ //判断如果当前没有数据了,等待生产者生产数据
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name+"---"+this.age);
//判断如果当前没有数据类,唤醒生产者线程,产生数据
this.flag = false ;
this.notify();
}
}
//----------------------------
public class SetThread implements Runnable {
private Student s ;
public SetThread(Student s){
this.s = s ;
}
int x = 0 ;
@Override
public void run() {
//模拟生产者不断产生数据
while(true){
if(x % 2 ==0){
s.set("张三",41);
}else{
s.set("李四",26);
}
x ++;
}
}
}
//----------------
public class GetThread implements Runnable {
private Student s ;
public GetThread(Student s ){
this.s = s ;
}
@Override
public void run() {
while(true){//模拟消费者不断使用数据
s.get();
}
}
}
//---------------------
public class ThreadDemo {
public static void main(String[] args) {
Student s = new Student() ;//创建学生对象
SetThread st = new SetThread(s) ;//生产者资源类/消费者资源类对象
GetThread gt = new GetThread(s) ;
Thread t1 = new Thread(st) ;//创建线程了对象
Thread t2 = new Thread(gt) ;
t1.start();
t2.start();
}
}
线程组
线程组 ThreadGroup 线程组表示一个线程的集合
hread类中方法:
public final ThreadGroup getThreadGroup():获取当前所有线程的默认线程组
ThreadGroup
public final String getName() :获取默认的线程组名称: (默认就是main)
构造方法:
ThreadGroup(String name):构造一个新的名称的线程组
所有的线程默认的线程组就是main线程(用户线程)
public class ThreadGroupDemo {
public static void main(String[] args) {
//method() ;
method2() ;
}
//设置线程组名称
private static void method2() {
// ThreadGroup(String name):构造一个新的名称的线程组
ThreadGroup tg = new ThreadGroup("myMain") ;
//创建线程类对象:可以将线程组对象作为参数传递
//public Thread(ThreadGroup group,String name)
Thread t1 = new Thread(tg,"线程1") ;
Thread t2 = new Thread(tg,"线程2") ;
System.out.println(t1.getThreadGroup().getName());//输出myMain
System.out.println(t2.getThreadGroup().getName());//输出myMain
}
线程池的使用Callable接口方式
1)接口:ExecutorService :跟踪一个或者多个异步任务
2)如何实例化---->Executors.newFixedThreadPool(int) 工厂方法:
Executors:工厂类
提供创建线程池对象的方法
public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService
方法:
Future<?> submit(Runnable task) :通过线程池提交异步任务
<T> Future<T> submit(Callable<T> task):提交异步任务
Future:异步任务计算的结果!
第三种方式:线程池实现---->还是自定义一个类 实现Runnable接口
//Callable要比Runnable接口更好
//可以跟踪具体的异常错误(如果执行过程中,线程出现异常,可以跟踪异常信息!)
public class ThreadDemo {
public static void main(String[] args) {
//创建一个线程池:静态工厂模式
ExecutorService pool = Executors.newFixedThreadPool(2);//两个线程
pool.submit(new MyCallable()) ;
pool.submit(new MyCallable()) ;
//使用完毕,关闭线程池--将底层产生的线程归还线程池中
pool.shutdown();
//-------------------
public class MyCallable implements Callable<Object> {
//MyCallabe:等待需要被线程池提交的异步任务
//call方法:本身要计算的结果
@Override
public Object call() throws Exception {
for(int x = 0 ; x <100 ; x ++ ){
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}
//------------------------
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int x = 0 ; x < 100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
两条线程分别求和
public class MyCallable implements Callable<Integer> {
private int number ;
public MyCallable(int number){
this.number = number ;
}
//call方法:异步结果结果:它的返回值需要和Callable泛型的数据类型一致
//最终结果变量:
int sum = 0 ;
@Override
public Integer call() throws Exception { //就是线程执行的耗时操作 类似于 Runnable的run方法
for(int x = 1 ; x <= number; x ++){
sum+=x ;
}
return sum;
}
}
//-------------------------
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建存有2条线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//Future:接口:异步计算的结果
Future<Integer> f1 = executorService.submit(new MyCallable(100));
Future<Integer> f2 = executorService.submit(new MyCallable(200));
//V get():获取结果值
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
}
}
定时器
j ava.util.Timer:定时器 :有线程安排执行务执行一次,或者定期重复执行。
构造方法:
public Timer() 无参构造方法
成员方法
public void cancel():取消定时器
public void schedule(TimerTask task,Date time) :在指定日期时间内执行这个任务
public void schedule(TimerTask task,long delay):在指定的延迟时间后执行task任务(时间:毫秒)
public void schedule(TimerTask task,
long delay,
long period) :在指定的delay延迟时间后开始重复时间间隔period来执行task任务
public class TimerDemo {
public static void main(String[] args) {
//创建一个定时器
Timer timer = new Timer() ;
// public void cancel():取消定时器
//参数1:定时任务:抽象类,定义子类继承自TimerTask
// timer.schedule(new MyTask(timer),3000); :3秒后执行一次这个任务
/* public void schedule(TimerTask task,
long delay,
long period) :在指定的delay延迟时间后开始重复时间间隔period来执行task任务*/
timer.schedule(new MyTask(timer),2000,3000);//2000指的是开始执行时间,3000指执行后再次执行时间,单位(毫秒)
}
}
//定时任务
class MyTask extends TimerTask{ //TimerTask实现Runnable接口 ---会使用同步机制
private Timer t ;
public MyTask(Timer t){
this.t = t ;
}
int i = 0;
@Override
public void run() {
System.out.println("bom...");
//关闭定时器
//t.cancel() ;
i++;
if (i == 10) {
System.out.println("停止");
t.cancel();// 增加判断,达到条件停止定时器
}
}
}
创建型设计模式之单例
单例: 在加载某个类的时候,内存中始终只有一个对象!(该类对象自动创建)
单例分两种:
饿汉式:特点 永远不会出现问题的单例模式!
在加载这个类的时候,就已经在内存中创建了一个对象!
1)自定义一个类(具体类)
2)成员位置:创建当前类的实例
3)提供私有的构造方法:外界不能够创建它
4)对外提供公共的访问方法:静态的,返回值当前类本身
懒汉式:可能出现问题的单例模式
1)自定义一个类(具体类)
2)成员位置:声明类型的变量
3)构造方法私有化
4)提供对外的公共访问方法:
需要判断:当前如果没有对象,new 对象
存在懒加载或者延迟加载!
可能出现多线程 安全问题:
标准单例模式之饿汉式:Runtime
public class RuntTimeDemo {
public static void main(String[] args) throws IOException {
Runtime runtime = Runtime.getRuntime();
// Runtime runtime2 = Runtime.getRuntime();
//System.out.println(runtime==runtime2);
int cpuSize = runtime.availableProcessors();
System.out.println(cpuSize);
runtime.exec("notepad") ;
runtime.exec("mspaint") ;
runtime.exec("calc") ;
}
}
public class Programmer {
private static Programmer pro = null ;
private Programmer(){}
public synchronized static Programmer getPro(){//静态同步方法:锁对象:当前类名.class属性
if(pro ==null){
pro = new Programmer() ;
}
return pro ;
}
}
懒汉
public class Tests {
public static void main(String[] args) {
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1==s2);
System.out.println("-----------------");
Programmer p1 = Programmer.getPro();//输出地址
Programmer p2 = Programmer.getPro();
Programmer p3 = Programmer.getPro();
System.out.println(p1==p2);
System.out.println(p1==p3);
}
}
//------------------
public class Student {
//创建该类实例---(类的实例变量)
private static Student s = new Student() ;
private Student(){} //外界不能创建对象
//提供对外的公共访问方法:静态的
public static Student getStudent(){
return s ;
}
}
创建型设计模式:对象的创建
Java提供:简单工厂模式 ---静态工厂方法模式
优点:不需要具体类创建具体实例,通过工厂类创建
弊端:一旦有一个新的类型增,修改工厂类!
需要提供一个工厂类,负责具体的实例的创建
public class Test {
public static void main(String[] args) {
//创建猫
Cat cat = new Cat() ;
cat.eat();
cat.sleep();
//狗
Dog dog = new Dog() ;
dog.eat();
dog.sleep();
//优化之后
Animal animal = AnimalFactory.createAnimal("dog"); //父类引用指向子类对象
animal.eat();
animal.sleep();
animal = AnimalFactory.createAnimal("cat") ;
animal.eat();
animal.sleep();
}
}
//----------------------
public class Animal {//定义父类
public void eat(){
System.out.println("动物都需要吃");
}
public void sleep(){
System.out.println("动物都需要休息");
}
}
//------------------------
public class Cat extends Animal {//猫继承动物类
public void eat(){
System.out.println("i can eat and eat mouse");
}
public void sleep(){
System.out.println("i can sleep ...");
}
}
//------------------------
public class Pig extends Animal {
@Override
public void eat() {
System.out.println("eat");
}
@Override
public void sleep() {
System.out.println("sleep");
}
}
//------------------------
public class AnimalFactory {
//构造方法私有化
private AnimalFactory(){} //外界不能创建当前类对象
/优化:利用多态:提供功能扩展
public static Animal createAnimal(String type){
if(type.equals("dog")){
return new Dog() ;
}else if(type.equals("cat")){
return new Cat() ;
}else if(type.equals("pig")){
return new Pig() ;
}else{
System.out.println("对不起,工厂类没有提供者动物的实例创建!");
}
return null ;
}
}
工厂方法模式:
对象创建的工作:有一个接口完成
接口的子实现类---->创建具体实例的工厂类
优点:面向接口编程: 结构非常清晰,维护起来容易
弊端:代码量增加了,需要编写额外的工厂类...
public class Test {
public static void main(String[] args) {
//没有使用工厂方法模式
//抽象类多态
Animal a = new Cat();
a.eat();
a = new Dog() ;
a.eat();
System.out.println("-----------------------------------");
//猫的工厂类
Factory f = new CatFactory() ;
Animal animal = f.createAnimal(); //new Cat()
animal.eat();
f = new DogFactoy() ;
Animal an = f.createAnimal();
an.eat();
}
}
//----------------
public abstract class Animal {
public abstract void eat() ;
}
//--------------------
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//------------------
public class CatFactory implements Factory{
@Override
public Animal createAnimal() {
return new Cat() ;
}
}
//----------------------
public class CatFactory implements Factory{//猫的工厂类 来实现 工厂接口
@Override
public Animal createAnimal() {
return new Cat() ;
}
}
//--------------------------
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头...");
}
}
//-----------------------------
public class DogFactoy implements Factory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
//---------------------------
public interface Factory { //工厂接口
//创建动物的实例
public abstract Animal createAnimal() ;
}
Day5
什么是异常
异常就是程序出了问题 体系结构 Throwable 子类 Error Exception Error:严重问题: 无法直接使用代码方式解决(内存溢出...(加载大量图片的时候)) Exception:程序出现的这种问题,是可以通过代码方式解决 RuntimeException:运行时期异常 举例: NullPointerException:都属于代码逻辑不严谨导致的! 只要不是运行时期的出现的都是属于编译时期异常
解决异常的方案:
try...catch...finally:标准格式 变形格式 try...catch try...catch...catch... try...finally 捕获异常的执行流程 1)首先执行try中的代码 2)如果程序在try中出问题了,执行catch语句----jvm创建当前异常类对象:异常类名 e = new 异常类名() ; 自动查看是否匹配catch语句中的异常对象,如果匹配,就执行处理的语句! throws:抛出异常
编译时期异常和运行时期异常的区别
编译时期异常:必须要进行处理,否则代码通过不了,运行不了! 如果编译时期异常的代码使用的捕获异常,调用者不需要在处理了! try...catch... (开发中:使用捕获) 如果使用throws:抛出异常 在方法声明上使用 调用者必须要抛出,否则报错! 运行时期异常: 调用者不需要显示处理,可以通过逻辑代码来优化 也可以显示处理 注意事项: 不管什么编译和运行时期,尽量在代码中使用try...catch处理...
捕获异常的标准格式:
try{ 可能出现问题的代码 }catch(异常类名 变量名){ 处理语句; }finally{ //一般情况 释放相关的底层系统资源 //io流中的流对象 (底层系统资源) //Statment:sql语句的执行对象 //PreparedStatement:预编译的对象 //Connection:数据库的链接对象 //ResultSet:结果集对象 } finally特点: 就是释放相关的系统资源,而且finally语句一定会执行的! 除非一种特例,在执行finally语句,jvm退出了! (System.exit(0)) //-------------------------- try{ String s = "2021-5-28" ; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ; Date date = sdf.parse(s) ; System.out.println(date); }catch (ParseException e){ //String str = e.getMessage();//Unparseable date: "2021-5-28" // String str = e.toString() ;//java.text.ParseException: Unparseable date: "2021-5-28" // System.out.println(str); //public void printStackTrace():跟踪堆栈打印错误输出流 e.printStackTrace() ; System.exit(0); }finally { System.out.println("这里的代码一定会执行..."); }
面试题
final跟return的执行
当前try...catch...finally:捕获异常的时候,如果catch语句里面有return语句,finally还会执行吗? 在前还是在后? finally一定会执行,在return 前 (将方法已经结束---catch语句中的return语句已经返回路径!) finally代码除非在执行前 jvm退出,----finally不会出现return,都是去释放资源... public class FinallyDemo2 { public static void main(String[] args) { System.out.println(getNum(10)); } private static int getNum(int i) { try{ i = 20 ; int b = 0 ; System.out.println(i/b); //出现异常了 }catch(Exception e){ i = 30 ; //i =30 return i ; //当前位置 已经形成返回路径 i = 30 }finally { i = 40 ; //return i ; } return i; // return 30 } }
面试题throws和throw的区别?
这两个都是属于"抛出" throws:抛出 1)抛出在方法声明上,而且方法声明上的异常类名可以跟多个,中间使用逗号隔开 2)throws的异常处理交给:调用者进行处理 3)throws表示抛出异常的一种可能性 4)throws后面跟的异常类名 throw:抛出 1)是在方法体中使用,并非在方法声明上使用(位置不一样) 2)throw的异常处理交给: 方法体中的逻辑语句判断 3)throw表单抛出的异常的肯定性(执行某段代码,一定会执行这个异常) 4)throw后面跟的异常对象: throw new XXXException() ;匿名对象
finally : 一般使用在异常处理中:捕获异常 格式 try...catch...finally 它里面的语句一定会执行(特例:在执行之前jvm退出) try...catch...finally---应用场景: 业务层(service)代码中 使用捕获异 在数据库访问层(dao)代码, 建议使用抛出throws
自定义一个异常类
异常体现的结构: Throwable error exception RuntimeException 很多运行时期异常 IOException,ParseException..编译时期异常 自定义一个类:继承自RuntimeException 继承自 Exception 提供有参构造方法(String message)/无参构造方法
public class ExceptionDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in) ; System.out.println("请输入学生成绩:"); int socre = sc.nextInt() ; //创建Teacher类对象 Teacher t = new Teacher(); t.checkStudentSocre(socre); } } //------------------- public class Teacher { //打分的功能 public void checkStudentSocre(int socre){ //分值:1-100 if(socre<0 || socre>100){ //非法数据 throw new MyException("学生成绩非法...") ; }else{ System.out.println("学生数据合法..."); } } } //-------------------- //自定义一个异常类 继承自RuntimeException public class MyException extends RuntimeException{ public MyException(){} public MyException(String message){ super(message) ; } }
异常的使用注意事项:
1)子类继承父类, 重写父类的方法的时候,如果父类的方法存在抛出异常,那么子类重写的方法的异常 要么跟父类方法异常保持一致 要么是父类方法异常的子类异常 2)子类继承父类的时候,子类重写父类方法,如果父类的该方法没有抛出异常,那么子类只能去try...catch,不能throws, 一旦子类抛出,那么父类的方法也必须抛出(不推荐更改父类的代码!)
public class ExceptionDemo2 { public static void main(String[] args) { } } class Father{ public void show(){ System.out.println("show father"); } } //子类要重写method方法 class Son extends Father{ public void show() { //将日期文本--->日期格式 //String--->Date try { String s = "2021-5-28" ; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ; Date date = sdf.parse(s) ; //throws----> 父类的方法也必须throws } catch (ParseException e) { e.printStackTrace(); } } }
文件和目录(文件夹)路径名的抽象表示形式。
java.io.File:文件和目录(文件夹)路径名的抽象表示形式。 构造方法 File(String parent, String child) File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File类的相关的文件夹的创建
文件的创建 文件夹以及文件的删除功能 boolean mkdir() :创建文件夹:如果已经存在,创建不成功,返回false! public boolean mkdirs():创建多级目录的使用,如果父文件夹不存在,自动创建 public boolean createNewFile()throws IOException:创建文件,可能出现IO异常(举例:文件找不到) public boolean delete():删除文件或者是文件夹
//创建一个demo文件夹 如果没有指定盘符:相对路径:默认在当前项目路径的下创建文件或者是文件 File file = new File("demo") ; System.out.println(file.mkdir()); File file2 = new File("demo\\a.txt") ; System.out.println(file2.createNewFile());
重命名功能
public boolean renameTo(File dest):对当前文件进行重命名 情况1: 当前指定文件路径和重命名后的路径一致的,那么仅仅是改名字 需要将D盘下的高圆圆.jpg改名个为杨桃.jpg D:\\高圆圆.jpg D:\\杨桃.jpg 情况2:如果路径不一致, ----改名并剪切 File file = new File("文件.jpg") ;//项目路径的文件 File file2 = new File("d:\\文件1.jpg") ;//新文件地址 System.out.println(file.renameTo(file2));//执行城后和将项目路径的文件转移至D盘下,改名位文件1
判断功能
public boolean canRead():判断这个文件是否是可读 public boolean canWrite():是否可写 public boolean exists():是否存在 public boolean isDirectory():是否是文件夹 (使用居多) public boolean isFile():是否是文件 (使用居多) public boolean isHidden():是否是隐藏文件
File类的高级获取功能:
public String[] list():当前目录下的所有的文件夹以及文件的字符串数组 public File[] listFiles():获取当前某个目录下的所有的File数组对象 需求: 获取d盘下的所有的以.jpg结尾的文件 1)获取当前某个盘符下或者目录下的所有的文件以及文件夹的file数组 public File[] listFiles():获取当前某个目录下的所有的File数组对象 2)防止空指针:判断如果当前File数组不为空的时候,遍历 遍历,获取到每一个File对象 2.1)File对象如果是文件 isFile() 2.2)并且它还是以.jpg结尾的文件 2.3)输出文件对象.getName() 获取到
File file = new File("d:\\") ;`选择盘符 File[] fileArray = file.listFiles(); if(fileArray!=null){ for(File f :fileArray){ //获取名称 if(f.isFile()){ //再次判断 if(f.getName().endsWith(".jpg")){//判断D盘有无.jpg结尾的文件,并打印 System.out.println(f.getName()); } } } }
File类获取功能:
使用文件名称过滤器来帮助我们完成过滤! 优点:在获取的时候就已经通过文件名称过滤器筛选了 获取字符串列表 public String[] list(FilenameFilter filter) 获取文件列表 public File[] listFiles(FilenameFilter filter) 形式参数接口:需要接口子实现类对象 使用匿名内部类的方式 FilenameFilter:文件名称过滤器 boolean accept(File dir,String name) 返回值true或者false取决于:是否将当前name文件添加在文件列表中
//描述D盘 File file = new File("d:\\") ; File[] fileArray = file.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { File file = new File(dir,name); //一步走 return file.isFile() && file.getName().endsWith(".jpg") ; } }); for(File f :fileArray){ System.out.println(f.getName()); }
递归
方法调用方法本身的现象而不是方法嵌套方法! 使用递归的前提条件: 1)必须有一个方法 2)必须存在出口条件(结束条件):否则就是死递归 3)有一定的规律
//需求:求出5的阶乘 private static int getNum(int i) { //出口条件:如果i=1,永远是1 if(i==1){ return 1 ; }else{ //i不等于1 return i*getNum(i-1) ; //5 * getNum(4) }