高级API

本文介绍了Java中的IO流操作,如字节流和字符流的创建,以及序列化与反序列化原理。同时涵盖了集合的泛型、主要接口及其操作,进程与线程的概念、状态切换和多线程编程的三种方式。此外,还讨论了数据安全隐患的解决方案和反射的基础应用。
摘要由CSDN通过智能技术生成

1. IO流

1. 学习方式:学习抽象父级的公共方法 学习子类流对象的创建方式
2. 流的分类
根据方向:输入流 输出流
根据操作单位:字节流 字符流
字节输入流InputStream:
InputStream--抽象父类--不能实例化
FileInputStream--文件字节输入流-FIS
BufferedInputStream--高效字节输入流-BIS

1	FIS in = new FIS(new File(路径));
2	FIS in = new FIS(路径);
3	BIS in = new BIS( new FIS(new File(路径)));
4	BIS in = new BIS(new FIS(路径));


字节输出流OutputStream:
OutputStream--抽象父类,不能实例化
FileOutputStream--文件字节输出流--FOS
BufferedOutputStream--高效字节输出流-BOS

 

1    FOS out = new FOS(new File(路径));  
2    FOS out = new FOS(路径);
3    BOS out = new BOS(new FOS(new File(路径))); 
4    BOS out = new BOS(new FOS(路径));


字符输入流Reader:
Reader--抽象父类--不能实例化
FileReader--文件字符输入流-FR
BufferedReader--高效字符输入流-BR

1	FIS in = new FIS(new File(路径));
2	FIS in = new FIS(路径);
3	BIS in = new BIS( new FIS(new File(路径)));
4	BIS in = new BIS(new FIS(路径));


字符输出流Writer:
Writer--抽象父类,不能实例化
FileWriter--文件字符输出流--FW
BufferedWriter--高效字符输出流--BW

  

1     FW out = new FW(File/File,append/String pathname/String pathname,append);
2    BW out = new BW(Writer--所以传的是子类FW(上面那4种));
  注意:这里的append参数表示流向文件输出数据的时候是追加还是覆盖,如果不写,默认false是覆盖,如果改为true,表示追加



3. 序列化与反序列化

序列化与反序列化的作用就是对象的保存与传输
序列化:把内存中的对象通过序列化流输出到磁盘中(比如文件里),使用的流是ObjectOutputStream【把数据写出到文件】
反序列化:通过反序列化流将磁盘中的数据恢复成对象,使用的流是ObjectInputStream【把之前写到文件里的数据读到程序中】
注意1:一个类的对象如果想被序列化,那么这个类必须实现可序列化接口
实现这个接口的目的是相当于给这个类做了一个标记,标记可以序列化
注意2:序列化时会自动生成一个UID,表示当前序列化输出的对象的版本信息
反序列化时会拿着当前的UID与之前序列化输出的UID做比较,一致,反序列化成功,不一致,报错
注意3: 所以,标准操作是一次序列化对应一次反序列化
如果目标对象所在的类没有做任何修改,一次序列化也可以对应多次反序列化(根本原因是UID没变)


2. 集合

1. 泛型
泛型通常与集合一起使用,用来约束集合中元素的类型
泛型< type >必须写引用类型而不是基本类型
泛型方法 public static == < E > == void get(E[] e){},两处位置都出现了泛型,缺一不可
2. 集合被称作Collection,是一个可以存放多个数据的容器,而且集合中提供了丰富的方法来操作集合中的元素
是集合层次的根接口,学习抽象父级的公共方法
3. Collection集合方法总结
单个集合的操作:

boolean add(E e) 将指定元素添加到集合中
void clear() 清空集合
boolean contains(Object o) 判断本集合是否包含指定的元素
boolean equals(Object o) 比较集合对象与参数对象o是否相等
int hashCode() 返回本集合的哈希码值
boolean isEmpty() 判断本集合是否为空
boolean remove(Object o) 从本集合中移除指定元素o
int size() 返回本集合中元素的个数
Object[] toArray() 将本集合转为数组

集合间的操作:

boolean addAll(Collection<> c) 将c集合中的所有元素添加到本集合中
boolean containsAll(Collection<> c) 判断本集合是否包含c集合的所有元素
boolean removeAll(Collection<> c) 移除本集合中属于参数集合c的所有元素
boolean retainAll(Collection<> c) 保留本集合与参数集合c的公共元素

集合的迭代:

Iterator iterator() 返回本集合的迭代器

4. List接口的特点
List集合是有下标的
List集合是有顺序的
List集合可以存放重复的数据


单个集合的操作:

void add(int index, E element) 在集合的指定下标index处插入指定元素element
E get(int index) 返回本集合中指定下标index处的元素
E remove(int index) 移除本集合中指定下标index处的元素
E set(int index, E element) 用参数元素element替换集合中指定下标index处的元素
int indexOf(Object o) 判断指定元素o在本集合中第一次出现的下标,如果不存在,返回-1
int lastIndexOf(Object o) 判断指定元素o在本集合中最后一次出现的下标,如果不存在,返回-1
List subList(int fromIndex, int toIndex) 截取子集合,包含formidex处的元素,不包含toIndex处的元素

集合间的操作与集合的迭代

boolean addAll(int index, Collection<> c) 将参数集合c中的所有元素,插入到本集合中指定的下标index处
ListIterator listIterator() 返回此列表元素的迭代器,这个是List自己的,不太常用,可以逆序迭代

5. List接口的两个常用实现类
ArrayList的特点:
1)底层的数据结构是数组,内存空间是连续的
2)元素有下标,通常可以根据下标进行操作
3)增删操作比较慢,查询操作比较快【数据量大时】
LinkedList的特点:
1)底层的数据结构是链表,内存空间是不连续的
2)元素有下标,但是通常首尾节点操作比较多
3)增删操作比较快,查询操作比较慢【数据量大时】
注意:LinkedList查询慢也不是都慢,首尾操作还是比较快的
简单方法:

void addFirst(E e) 添加首元素
void addLast(E e) 添加尾元素
E removeFirst() 删除首元素
E removeLast() 删除尾元素
E getFirst() 获取首元素
E getLast() 获取尾元素
E element() 获取首元素

功能一致但是名字不太好记的方法:

boolean offer(E e) 添加尾元素
boolean offerFirst(E e) 添加首元素
boolean offerLast(E e) 添加尾元素
E peek() 获取首元素
E peekFirst() 获取首元素
E peekLast() 获取尾元素
E poll() 返回并移除头元素
E pollFirst() 返回并移除头元素
E pollLast() 返回并移除尾元素

6. Map接口
Map接口的特点
1. map集合的结构是:键值对、KEY与VALUE、Map.Entry<K,V>的映射关系
2. map中key值不允许重复,如果重复,对应的value会被覆盖
3. map中的映射关系是无序的
4. map没有自己的迭代器,所以迭代时通常需要转成set集合来迭代
简单方法:

void clear() 清空集合
boolean equals(Object o) 判断集合对象与参数o是否相等
int hashCode() 返回本集合的哈希码值
boolean isEmpty() 判断集合是否为空
int size() 返回本集合中键值对的个数

map单个集合间的操作

boolean containsKey(Object key) 判断map中是否包含指定的key
boolean containsValue(Object value) 判断map中是否包含指定的value
V get(Object key) 根据指定的key返回对应的value,如果不存在,返回null
V remove(Object key) 删除本集合中参数key对应的键值对
V put(K key, V value) 向集合中添加映射关系(键值对)
void putAll(Map<> m) 向本集合中添加m集合的所有映射关系(键值对)

map的迭代

Collection values() 把本map中的Value值取出放入一个Collection中并返回这个Collection
Set keySet() 把本map中的Key值取出放入一个Set集合中并返回这个Set集合
Set<Map.Entry<K,V>> entrySet()
把本map中的每一对KV都看成是一个Entry,把所有的Entry取出放入一个Set集合中并返回这个Set集合

7. Set接口
Set接口的特点
set集合没有重复的元素
set集合的元素是无序的
set集合可以存null值,并且null最多有一个
我们自定义对象如果想去重,需要在自定义类中添加重写的equals()与hashCode()
8. 集合学习的方法
学习父级的公共方法,学习子类的创建方式,学习各种集合的特点
关于List大多都是与下标有关的操作
关于Set通常都是去重的操作
关于map通常都是映射关系,也就是键值对
API要常练习,方法互相之间没有任何关系,用哪个,查哪个


3. 进程与线程

1. 程序:数据与指令的集合,而且程序是静态的

2. 进程:运行中的程序,给程序加入了时间的概念,不同时间进程有不同的状态,进程是动态的,代表OS中正在运行的程序
进程有独立性,动态性,并发性

3. 并行:相对来说资源比较充足,多个CPU同时并发处理多个不同的进程

4. 串行:相对来说资源不太充足,多个资源同时抢占公共资源,比如CPU

5. 线程:线程是OS能够进行运算调度的最小单位
一个进程可以拥有多个线程,当然,也可以只拥有一个线程,只有一个线程的进程称作单线程程序
注意:每个线程也有自己独立的内存空间,当然也有一部分共享区域用来保存共享的数据

6. 线程的几种状态以及线程状态之间的切换
1)新建状态:创建线程对象,申请PCB,对应的是new线程对象
2)就绪状态/可运行状态:万事俱备,只欠CPU,刚刚创建好的线程对象所有资源已经准备好,并且加入到了就绪队列之中
唯有等待操作系统的调度,只要分配了CPU,也就是时间片,当前线程可立即执行,对应的是start()
注意:调用start()并不会立即执行线程对象,这个是由OS的调度规则决定的。我们控制不了
3)执行/运行状态:就绪队列中的线程对象被OS选中,分配了时间片,正在执行
注意:只有就绪状态才能变成运行状态
4)阻塞状态:线程在执行过程中遇到了问题,比如锁阻塞、休眠阻塞、等待阻塞…
注意:我们的阻塞状态,等问题解决了以后/获取了临界资源【要抢占的公共资源】后
是加入到就绪队列中的,转为就绪状态,而不是转为运行状态直接执行
5)终止状态:线程成功执行完毕,释放资源,归还PCB
6)线程的挂起:正在运行中的线程,由于CPU分配的时间片已经用完,所以需要冻结当前线程运行的状态与各项信息
把它插入到就绪队列中,直到下次这个线程被调度执行时,重新恢复现场,继续执行

在这里插入图片描述
7. 多线程编程实现方案一:extends Thread继承方式
1)自定义一个多线程类用来继承Thread类
2)重写run()里的业务【这个业务是自定义的】
3)创建线程对象【子类对象】
4)通过刚刚创建好的自定义线程类对象调用start()
注意1:不能调用run(),因为这样调用只会把run()看作一个普通的方法,并不会以多线程的方式启动程序
而且调用start()时,底层JVM会自动调用run(),执行我们自定义的业务
注意2:我们除了可以调用默认的父类无参构造以外,还可以调用Thread(String name),给自定义的线程对象起名字,相当于super(name);

8. 多线程编程实现方案二:implements Runnable 实现方式
1)自定义一个类实现接口Runnable
2) 实现接口中唯一一个抽象方法run()
3) 创建接口实现类的对象,这个对象是作为我们的目标业务对象【因为这个自定义类中包含了我们的业务】
4)创建Thread类线程对象,调用的构造函数是Thread(Runnable target)
5)通过创建好的Thread类线程对象调用start(),以多线程的方式启动同一个业务target
注意1:由于Runnable是一个接口,无法创建对象,所以我们传入的目标业务类,也就是接口实现类的对象
注意2:只有调用start()才能把线程对象加入到就绪队列中,以多线程的方式启动,但是:
接口实现类与接口都没有这个start(),所以我们需要创建Thread类的对象来调用start(),并把接口实现类对象交给Thread(target);
大家可以理解成“抱大腿”,创建的是Thread类的线程对象,我们只需要把业务告诉Thread类的对象就好啦
使用方式二的优势:
1)耦合性不强,没有继承,后续仍然可以继承别的类
2)采用的是实现接口的方式,后续仍然可以实现别的接口
3)可以给所有的线程对象统一业务,业务是保持一致的
4)面向接口编程,有利于我们写出更加优雅的代码

9. 多线程编程实现方案三:Executors 创建线程池的方式
1)创建线程池的工具类:Executors.newFixedThreadPool(int n);可以创建包含最多n个线程的线程池对象
2)创建好的线程池对象:ExecutorService
使用pool.excute()来讲线程池中的线程以多线程的方式启动,每次调用都会将一个线程对象加入到就绪队列之中
这个线程池对象负责: 新建/启动/关闭线程,而我们主要负责的是自定义的业务
注意:线程池是不关闭的,实现的效果就是线程池中线程对象的随取随用,这样就避免了频繁的创建与销毁线程,不会造成资源浪费
3)合理利用线程池可以拥有的优势:
1. 降低系统的资源消耗:减少系统创建与销毁线程对象的次数,每个线程都可以重复利用,执行多次任务
2. 提高响应速度:当任务到达时,任务可以不用等待线程创建就能立即执行
3. 提高线程的可管理性:可以根据系统的承受能力,调整线程池中线程的数目
防止因为创建多个线程消耗过多的内存导致服务器的崩溃
【每个线程大约需要1MB的内存,线程开的越多,消耗的内存也就越大,最后死机】

10. 多线程数据安全隐患的解决方案
1)出现数据安全问题的原因:多线程程序 + 多个线程拥有共享数据 + 多条语句操作共享数据
2)解决:加锁synchronized同步关键字
1. 同步方法【不太常用】,格式:在方法的定义上加synchronized
2. 同步代码块,格式:

        

synchronized(唯一的锁对象){
				可能会出现数据不安全问题的所有代码
		}


  注意1:锁对象必须唯一!!!
  比如:如果是实现接口的方式,只需要创建一个接口实现类对象。而这个对象当做的是目标业务对象,类中定义的锁对象自然也只有一个
  比如:如果是继承Thread类的方式,我们需要创建多个子类对象作为线程对象
            那这个时候不同的线程对象间应该共享同一把锁,所以需要把锁设置为静态,被全局所有对象共享
            而且建议,此种方式使用的锁对象是本类的字节码对象:类名.class
  注意2:加锁时,同步代码块的范围需要考虑, 不能太大,太大效率太低;也不能太小,太小锁不住
  注意3:加锁时,锁对象的类型不做限制,只要保证锁对象唯一即可

同步与异步
异步:是多个线程抢占资源的效果,不排队,所以效率高,但是数据不安全
同步:每次只有一个线程独占资源,排队,所以效率低,但是安全,为了安全必要应该牺牲一部分资源

4. 注解

1. JDK自带的注解(5个)
要求大家掌握的是@Override注解,这个注解可以加在方法上,用来表示这是一个重写的方法
2. 元注解5个
元注解是用来定义其他注解的注解,也就是说,注解的语法与JAVA不同,是靠注解来定义的
定义注解的格式:@interface 注解名
可以根据元注解对注解进行设置:
表示被描述的注解可以使用的位置:值可以多选 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
表示被描述的注解的声明周期:注意值只能3选1 @Retention(RentionPolicy.RUNTIME/SOURCE/CLASS)
3. 自定义注解
我们也可以根据自己的需求来定义个性化的注解:使用@interface 注解名来定义的,主要使用的就是上面的两个元注解
除此之外,我们还可以给注解加功能,比如注解的属性:
格式:属性类型 属性名(); 比如:int age();
注意:定义了注解的普通属性以后,使用注解时必须给属性赋值,格式:@Rice(age=10)
如果给属性设置了默认值,那么使用注解时就不需要给属性赋值了,格式:int age() default 0;
我们还可以给注解添加特殊的属性value,注意这个属性名字必须是value,类型不作限制
注意:特殊属性如果不设置默认值,使用注解时也需要赋值,不过赋值可以简写,比如@Rice(“apple”)
特殊属性赋予默认值后,就可以直接使用注解了,赋予默认值的格式:String value() default “apple”;
注意:如果有多个属性,并且都没有赋予默认值,那么使用注解时的格式:@Rice(value=“apple”,age=10)


5. 设计模式

1. 概念:是一些前人总结出来的值得学习的编程“套路”,设计模式一共有23种
2. 单例设计模式:确保代码中本类的实例只有一个
3. 实现思路:
方案一:饿汉式
1)把本类的构造方法私有化–为了不让外界调用构造函数来创建对象
2)通过本类的构造方法创建对象,并把这个对象也私有化,为了防止外界调用
3)提供公共的全局访问点向外界返回本类的唯一的一个对象
注意:公共方法需要设置成静态–需要跳过对象,通过类名直接调用这个返回本类对象的公共方法
对象也需要设置成静态的–这个对象需要在静态方法中被返回,而静态只能调用静态
方案二:懒汉式
==延迟加载的思想:==我们有的时候有些资源并不是需要第一时间就创建出来,所以需要延迟到需要时再创建,这样既可以提升性能,又可以节省资源
1)把本类的构造方法私有化–为了不让外界调用构造函数来创建对象
2)创建了一个本类类型的引用类型变量【这个变量后续用来保存创建出来的对象的地址值】
3)提供公共的全局访问点向外界返回本类的唯一的一个对象
注意:这个公共的方法里,需要做判断
如果引用类型的变量值为null,说明:之前没有创建过本类对象–创建后再赋值给引用类型变量,并把它返回
如果引用类型的变量值不为null,说明:
之前有创建过本类对象,这个引用类型变量保存就是地址值,本次不再新建对象,直接返回


6. 反射

1. 反射的概念
反射是Java这门语言中比较有特点的一个特征,反射非常强大,我们可以通过反射获取目标类当中的资源,甚至是私有资源
不仅仅如此,我们甚至还可以使用资源,并且创建对象,所以反射是一个经常被使用到的技术
开发过程中,我们有的时候并不能拿到源代码,但是又需要使用资源,那这个时候反射的出现就很有必要了
2. 反射需要用到的API
获取字节码对象
Class.forName(“类的全路径”); 注意:传入的是类的全路径名,包含包名.类名,而且会抛出异常
类名.class 注意:这个写法需要自己手动接一下获取到的字节码对象,不能用快捷方式的
对象.getClass(); 注意:经常与匿名对象一起使用
获取包名 类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)
获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.getConstructor(int.class,String.class)//要先获取构造方法
c.newInstance(666,”海绵宝宝”);//通过获取到的构造函数对象,创建目标类对象
反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
field.setAccessible(true);//使私有成员允许访问
field.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
field.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

3. 关于反射的学习方式
如果能够直接操作源码,就不需要使用反射
反射经常用于源码与三大框架底层之中,熟练掌握有助于编程思想的提高
反射的思路与正常使用略有区别,所以需要多练习,掌握这些写法
重点参考笔记中的8个单元测试方法
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值