文件操作
查看文件
public class 查看文件 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件夹");
String next = scanner.nextLine();
File file = new File(next);
getall(file,0);
}
private static void getall(File file,int level) {
for (int i = 0; i < level; i++) {
System.out.print("\t");
}
System.out.println(file.getName());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
getall(f,level+1);
}
}
}
}
复制文件
public class 复制文件夹 {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件");
String src = scanner.next();
System.out.println("请输入目标");
String dest = scanner.next();
File file = new File(src);
File file2 = new File(dest,file.getName());
getall(file, file2);
}
private static void getall(File src, File dest) throws FileNotFoundException, IOException {
if (!dest.exists()) {
dest.mkdirs();
}
File[] listFiles = src.listFiles();
if (listFiles!=null) {
for (File f : listFiles) {
if (f.isFile()) {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(f));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(new File(dest,f.getName())));
int len = 0;
byte[] by = new byte[1024];
while((len = inputStream.read(by))!=-1){
outputStream.write(by, 0, len);
}
outputStream.close();
inputStream.close();
}else {
getall(f, new File(dest,f.getName()));
}
}
}
}
}
删除文件
public class 删除文件夹 {
public static void main(String[] args) {
Scanner scanner= new Scanner(System.in);
String nextLine = scanner.nextLine();
File file = new File(nextLine);
delect(file);
System.out.println("删除成功");
}
private static void delect(File file) {
File[] listFiles = file.listFiles();
if (listFiles!=null) {
for (File f : listFiles) {
if (f.isFile()) {
f.delete();
}else{
if (f.delete()) {
f.delete();
}else {
delect(f);
f.delete();
}
}
}
}
}
}
IO流
总结
1.输入流是从外部文件输入到内存;输出流是内存到文件
2.程序中的输入输出都是以流的形式保存的,流中保存的实际上都是字节文件
3.阻塞方法是指在程序调用该方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句。
4.流的底层都是字节。字符流是字节流的包装,方便直接接受字符串,它内部也是转成字节,写入底层。
5.尽量使用Buffered带有缓冲的类
多线程
java中使用的是抢占式调度
实现多线程编程的方式
1.继承Thread类:不能多继承。
public class Demo extends Thread{
@Override
public void run() {
//重写run方法
super.run();
}
public static void main(String[] args) {
Demo demo = new Demo();
//开启线程,让线程开始执行,让jvm调用run方法
demo.start();
}
}
2.实现Runnable接口:可能多实现。
public class Demo {
public static void main(String[] args){
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
在线程类中,实例变量有共享与不共享之分。三个线程同时方法一个共享变量就会出现线程安全问题。
使用synchronized 同步关键字,让线程排队执行run方法。
@Override
synchronized public void run() {
// TODO Auto-generated method stub
}
sleep()与wait()的区别
当一个Synchronized方法中调用sleep(),此时线程休眠,对象锁机制不会被释放,其他线程无法访问这个对象。进入就绪状态需要自己抢占cpu。
调用wait(),休眠对象,也会释放锁机制,其他线程可以访问该对象。
wait()方法和notify()方法
执行wait()后,休眠并释放锁机制,进入到与这个对象相关的等待池中。
被notify()唤醒时,等待池中的线程就被放到锁池中,线程获得锁,就会回到wait的中断地方。
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
线程池
线程池就是一个拥有多个线程的容器,其中的线程可以反复使用,减少了频繁创建线程、销毁线程的操作,减少资源消耗。
线程池优点
- 提高效率,创建好放入线程池中,使用直接拿,比创建快
- 方便管理,可以对线程池中线程统一管理
Exectutor顶级抽象类,是一个工厂类
****** ** ThreadPoolExecutor**
************** ** SingleThreadPool**:单例线程池
************** ** FixedThreadPool**:固定数量的线程池
************** ** CachedThreadPool**:缓存线程池,小型程序使用
****** ** ScheduledThreadPoolExecutor**:定期执行任务
************** ** SingleScheduledThreadPool**:单例延迟线程池
************** ** ScheduledThreadPool**:定时线程池
Exectutor创建线程池,失去了线程池的灵活性,而且存在一定的隐患,存在资源耗尽的可能,有最大容量的限制,也存在内存溢出的风险。
线程池都有哪几种工作队列
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
核心参数
corePoolSize:核心池的大小。
maximumPoolSize:线程池最大线程数
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
unit:参数keepAliveTime的时间单位
workQueue:一个阻塞队列,用来存储等待执行的任务
threadFactory:用于设置创建线程的工厂
handler:表示当拒绝处理任务时的策略
高并发
- 使用sychronized关键字,但是会十分影响效率
- 乐观锁,读不会冲突,写会冲突。读远大于写的场景
- 对同一个账号限制一次请求
- 对同一ip限制一次请求
- 前多少存进缓存服务器,后面请求等待。
锁
产生死锁四个条件
互斥
占有且等待
不可抢占
循环等待
乐观锁VS悲观锁
悲观锁:自己在使用数据的时候一定有别的线程来修改数据,因此获取数据时候先加锁,确保数据不会被别的线程修改。synchronized关键字和Lock的实现类都是悲观锁。
乐观锁:自己在使用数据的时候不会有别的线程修改数据,所以不会加锁,只在更新数据的时候去判断之前有没有线程更新了这个数据。数据没有更改,写入成功;数据被修改,执行不同操作。最常用的CAS算法。
独享锁/共享锁
独享锁:该锁一次只能被一个线程持有
共享锁:该锁可被多个线程持有
ReentrantLock、Synchronized是独享锁。
ReadWriteLock读是共享锁、写是独享锁
可重入锁
递归锁:指在同一个线程在外层方法获取锁定时候,进入内层方法会自动获取锁。ReetrantLock、Synchronized都是可重入锁。
公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。ReetrantLock、Synchronized都是非公平锁。
偏向锁、轻量级锁、重量级锁、自旋锁的概念
偏向锁:如果一个线程获得了锁,那么锁就进入了偏向模式。当这个线程再次请求锁时,无需再做任何同步操作。
轻量级锁:简单的将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。
重量级锁:指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低
自旋锁:轻量级锁就会膨胀为重量级锁后,虚拟机为了避免线程真实的在操作系统层面挂起,虚拟机还会在做最后的努力
JVM
类加载机制
加载:类加载器获二进制字节流,将静态存储结构转化为方法区的运行时数据结构,并生成此类的Class对象。
验证:验证文件格式、元数据、字节码、符号引用,确保Class的字节流中包含的信息符合当前虚拟机的要求。
准备:为类变量分配内存并设置其初始值,这些变量使用的内存都将在方法区中进行分配。
解析:将常量池内的符号引用替换为直接引用,包括类或接口的解析、字段解析、类方法解析、接口方法解析。
初始化:执行类中定义的Java程序代码(字节码)。
java回收机制
程序中主动调用System.gc()强制执行的GC为full GC;
强引用:默认情况下,对象采用的均为强引用;
软引用:适用于缓存场景(只有在内存不够用的情况下才会被回收)
弱引用:在GC时一定会被GC回收
虚引用:用于判断对象是否被GC
新特性
java7新特性
- Switch中使用String
- try-with-resources(1.7以前对文件流等操作需要手动释放资源,7以后可以自动释放资源)
- 捕获多个异常,使用 | 隔开
try {
result = field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
- 泛型实例化类型自动推断
List<String> list = new ArrayList<>();
- 增加二进制表示
- 数字中可添加分隔符,可以出现 _ 作为分隔符
int intOne = 1_000_000;
long longOne = 1_000_000;
double doubleOne = 1_000_000;
float floatOne = 1_000_000;
java8新特性
- 接口的默认方法
允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
- Lambda 表达式
提高了阅读性
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Collections.sort(names, (String a, String b) -> b.compareTo(a));
Collections.sort(names, (a, b) -> b.compareTo(a));
- 函数式接口
有且仅有一个抽象方法的接口,适用于Lambda使用的接口。使用@FunctionalInterface注解 - 方法与构造函数引用
- Lambda 作用域
访问局部变量:可以直接在lambda表达式中访问外层的局部变量。不允许声明一个与局部变量同名的参数或者变量。被引用的变量的值不可修改。
final int num = 1;
Converter<Integer, String> s =
(param) -> String.valueOf(param + num);
s.convert(2); // 3
public void repeat(String string, int count) {
Runnable runnable = () -> {
for (int i = 0; i < count; i++) {
string = string + "a";//编译出错
System.out.println(this.toString());
}
};
new Thread(runnable).start();
}
访问对象字段与静态变量:Lambda内部对于实例的字段(即:成员变量)以及静态变量是即可读又可写。
class LambdaDemo {
static int myStaticNum;
int myNum;
void testScopes() {
Converter<Integer, String> s1 = (param) -> {
myNum = 33;
return String.valueOf(param);
};
Converter<Integer, String> s2 = (param) -> {
myStaticNum = 87;
return String.valueOf(param);
};
}
}
访问接口的默认方法:无法访问到默认方法的
- Date API
- Annotation 注解