Java基础总结
Java基础知识总结
1. 集合
1.1集合与数据的区别
- 数据的容量是固定的一旦容量固定就不可更改,集合的容量是可变的,可以动态扩容
- 数组声明了它容纳元素的类型如int[] char[] Integer[] 等,集合可以不用声明,可以使用泛型
- 数组是java语言内置的数据类型,是线性排列的,执行效率和类型检查更快
1.2 集合的大致体系结构
- 集合分为两大类:单列集合和双列集合
单列集合:该集合体系中的每个元素都是一个值(Collection)
双列集合:该集合体系中每个元素都有两个值组成分别是key键和value值两者之间存在着映射关系(Map)- 单列集合的顶级接口是Collection双列集合的顶级接口是Map
- Collection接口下又分为两个派系:List接口和Set接口
- List接口的特点是:有序有重复,Set接口的特点是:无序无重复
- List接口下有三个常用的实现类:ArrayList、Linked、Vector
- Set接口下有两个常用的实现类:HashSet、TreeSet,其中LinkedHashSet实现类继承于HashSet
- Map接口下分为两个派系TreeMap类和HashMap类,而其中LinkedHashMap实现类继承于HashMap
1.3 集合中个具体实现类的特色
ArrayList
底层用数组实现,一般来说查询效率高,增删改效率低
其中扩容机制:创建一个原数组长度1.5倍的新数组进行拷贝
LinkedList
底层用双向链表实现,一般来说增删改效率高,查询效率低
Vector
同ArrayList一样,但内部几乎所有方法用synchronize修饰,是线程安全版本的ArrayList
底层扩容机制为2.0倍
HashSet
底层用HashMap实现,其实就是在HashMap的基础上置value为空
在添加元素的时候通常会采用hash与equals判断是否重复,如果重复则放弃添加
TreeSet
底层用HashTree实现
排序是借助comparebre和comparetor这两个接口,这两个接口有一下区别
compareble接口是内比较器,用于在类的内部设置排序规则,一般用于Tree集合
comparetor接口是外比较器,一般用于集合的sort方法中,一般直接通过匿名内部类的方式实现
HashMap
在JDK1.7之前是用数组+链表组成的hash表,而到了JDK1.8就用数组+链表+红黑树组成的hash表
在添加元素的时候HashMap底层会采用很多复杂的扩容机制
大略的扩容机制如下:
在添加元素的时候HashMap主要从两个方面进行扩容
- 扩容hash表的桶位(0.75):HashMap的初始容量一般为16(不指定初始容量的情况下),第一次扩容就是,当HashMap中元素数量达到了16的0.75倍的话就将桶位数量扩容至原来的两倍也就是32。
- 将链表红黑树化:树化有两个临界值,一个是树化阈值,一个是树化容量,只有当链表上的元素个数达到了树化阈值(8)的时候,会再次确认是否达到了树化容量(64),只有当两个条件都满足才进行红黑树化
TreeMap
此实现类的主要优点就是可以自动排序,通用借助comparable和comparator接口
2. IO流
2.1、流的概念
流是指内存与存储设备之间传输数据的通道。
其中I 表示input 输入 O 表示output 输出,这里的入和出是以内存(JAVA程序为参照),要实现JAVA程序和外部磁盘文件之间的数据交互,首先需要在这两者之间建立一个数据的通信管道,然后让我们的数据在管道中进行流通,因此我们将这样的技术形象的称为“流技术”,如果是从JAVA程序到外部磁盘称为输出或者写操作,如果是从外部磁盘向JAVA程序中流动则称为输入或者读操作
2.2、流的分类
按方向:以内存为参照物
- 输入流,指从存储设备输入到内存(读文件)
- 输出流,指从内存输出到存储设备(写文件)
按单位:
- 字节流,以字节为单位来处理所有文件
- 字符流,以字符为单位来处理文本文件
按功能:
- 节点流,普通的文件处理
- 过滤流,增强型的文件处理(读文件每次读取一行)
按构造也能分为:基本流和包装流
2.3、IO流的使用
流的四大家族(流的四个顶级父类)
InputStream (字节输入流)
OutputStream (字节输出流)
Reader (字符输入流)
Writer(字符输出流)
FileInputStream 文件字节输入流
FileOutputStream 文件字节输出流
字节过滤流,对原来的字节流添加了一个缓冲,操作与原来几乎一样
BufferedInputStream 字节过滤输入流
BufferedOutputStream 字节过滤输出流
ObjectInputStream 对象输入流 反序列化
ObjectOutputStream 对象输出流 序列化
FileReader字符输入流
FileWriter字符输出流
BufferedReader缓冲字符输入流(包装流)
BufferedWriter缓冲字符输出流(包装流)
InputStreamReader转换字符输入流(可指定编码类型)
OutputStreamWriter转换字符输出流(可指定编码类型)
3. 多线程
3.1 进程与线程
- 进程: 进程是程序的一次动态执行的过程,它经历了从代码加载,执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。程序是一个静态的概念,进程是一个动态的概念。
- 线程:线程依附于指定的进程,并且可以快速启动以及并发执行。一个进程中可以包含多个进程,至少包含一个,每个线程就是这个进程的一条执行路径,每个JAVA程序默认包含有一个主线程(运行main方法的)。
- 进程可以是线程的容器,进程不完成具体的指定执行,Java指令都是通过线程去执行的,线程是CPU进行调度的最小单位。
3.2 并行和并发
- 并行: 在多核CPU下每个核心单独负责一个进程,也就就是能够同时处理多个事情的能力。
- 并发: 在电脑中CPU一般会将执行时间分成很多个很小的时间片,然后将这些时间片一次分给不同的线程去执行,并且在多个线程中来回快速的切换,在切换的过程中由于时间片很小,人一般感受不到,就会感觉到任务在同时进行。总的来说并发就是拥有处理多个事情的能力,但不是一起处理,宏观上是并行,微观上是串行。
3.3 线程的三种创建方式
- 继承Thread类,重写run()方法
- 实现Runnable接口,重写run()方法
- 实现Callable接口,重写call()方法
- 建议使用Runnable接口的这种方式,实现解耦,与高可用
- Callable的call方法与Runnable接口的run()的主要区别是call方法有String的返回值还能抛异常
3.4 线程的常用方法
- setName(String name) 设置当前线程的名称,系统默认为每个线程取一个名字:Thread-num,后期程序中就可以通过getName方法获取当前线程的名称
- currentThread() 这个方法不是线程对象的方法,属于线程类的一个静态方法 Thread.currentThread(),始终获取的是调用这个方法所在的线程对象,一般可以通过此对象调用getName()获得线程名称
- sleep(long 毫秒数) 也是一个线程类的静态方法,让调用该方法的线程进入到休眠的状态,不再和其他的线程一起抢占CPU的时间片
此方法有两种解除休眠的情况:一种是睡够了时间 另一种是被打断了(打断的时候会抛出一个异常)- yield()是一个本地方法,让当前线程放弃CPU的执行权,重新回到执行的等待队列
- join()线程合并,还可以指定等待的时间,传入一个毫秒数
- setPriority(Thread.MIN_PRIORITY);设置线程的优先级 1 5 10
3.5 死锁代码实例
public class DeadLockTest {
private static String lock1 = "a";
private static String lock2 = "b";
public static void main(String[] args) {
new Thread(() -> {
sleepStu();
},"张三").start();
new Thread(() -> {
studyStu();
},"李四").start();
}
public static void sleepStu(){
synchronized (lock1){
System.out.println(Thread.currentThread().getName() + "正在睡觉");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2){
System.out.println(Thread.currentThread().getName() + "休息完毕去学习");
studyStu();
}
}
}
public static void studyStu(){
synchronized (lock2){
System.out.println(Thread.currentThread().getName() + "正在学习");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1){
System.out.println(Thread.currentThread().getName() + "学习累了准备休息");
sleepStu();
}
}
}
}
4. 网络编程
4.1 计算机网络分层
1、实体层(物理层)
物理层说白了就是那些连线,光纤、双绞线之类的。
2、链接层(数据链路层)
他也是计算机网络的低层,他的作用就是将网络层交下来的数据封装成帧交给物理层,以及将从物理层接收的帧解析出数据交给网络层。(ps:数据在物理层一般叫帧,在网络层交IP数据报或者包)。像适配器、转发器、集线器、网桥、交换机都被归在链接层。
3、网络层
网络层的作用是向上层提供简单灵活的、无连接的、尽最大努力交付的数据报服务,它不提供服务质量的承诺,它是为主机间提供逻辑通信。这里涉及到地址解析,路由等内容。常见的路由器可以归为网络层。
因特网的网络层包括著名的IP协议,该协议定义了数据报中的各个字段以及端系统和路 由器如何作用于这些字段。仅有一个IP协议,所有具有网络层的因特网组件都必须运行IP协议。
4、运输层
运输层是为应用进程之间提供端到端的逻辑通信。传说中的TCP三次握手、四次握手就发生在这里。这里需要重点关注。
常见协议:① 传输控制协议TCP (Transmission Control Protocol)
② 用户数据报协议UDP (User Datagram Protocol)
5、应用层
域名解析、HTTP、电子邮件等等都是应用层的范畴。应用层的协议比较多,我们重点关注HTTP 协议。
4.2 TCP协议与UDP协议
TCP协议的特点:
通信的双方在进行信息的传输之前必须首先建立好连接,在连接过程中有一个三次握手的动作,保证通信的可靠,一旦建立好连接可以发送大量的数据,在通信结束的时候要关闭连接(四次挥手),相比来说效率比较低
** UDP协议的特点:**
通信之前无需建立双方的连接,通信结束后也无需进行资源的释放,通信效率比较高,UDP协议的通信是不可靠的,传输的过程中可能会发生数据的丢失,发送方也不关系接受者是否成功的接受到消息,每次发送的数据只能在64K的范围内
5. 反射和注解
5.1 反射
5.1.1 获取class对象的常见方式
- 通过类名.class
- 通过对象.getClass
- 通过Class.forName(“全类名”)
- 通过类的加载器
//方式一:调用运行时类的属性:.class(可以通过泛型进行约束)
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)(重要)
Class clazz3 = Class.forName("类的完整名称");
System.out.println(clazz3);
//了解即可
//方式四:类的加载器:ClassLoder
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("类的完整名称");
System.out.println(clazz4);
5.1.2 获取类的结构
- 获取所有属性(包括父类的),但是只能获得public修饰的属性
Field[] fields = clazz.getFields(); - 获取所有自己所声明的属性,可以获得任何修饰符
Field[] f1 = clazz.getDeclaredFields(); - 获取所有方法(包括所有父类中的所有方法)但是只有public属性修饰的方法
Method[] methods = clazz.getMethods(); - 获取所有方法(获取自身所有说明的方法)不包含父类的
Method[] declaredMethods = clazz.getDeclaredMethods(); - 获取运行的当前实例中public的构造器
Constructor[] constructors = clazz.getConstructors(); - 获取当前运行实例中所有声明的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); - 获取运行时类的接口
Class[] interfaces = clazz.getInterfaces(); - 能够获取私有化属性、方法、构造器
.setAccessible(true);
5.1.3 调用运行时类的指定的结构(属性方法构造器)
调用指定的属性
//获取Class对象
Class clazz = Class.forName("com.qianfeng.entity.Student");
//创建运行时类的对象
Student s = (Student)clazz.newInstance();
// //获取指定的属性(要求属性设置为public)(通常不使用)
// Field s_id = clazz.getField("s_id");
//获取指定的属性
Field s_id = clazz.getDeclaredField("s_id");
//包装当前属性是可访问的
s_id.setAccessible(true);
//设置当前属性的值
s_id.set(s,"1001");
//获取当前属性的值
String o = (String) s_id.get(s);
System.out.println("o = " + o);
调用指定的方法
//获取Class对象
Class clazz = Class.forName("com.qianfeng.entity.Student");
//创建运行时类的对象
Student s = (Student)clazz.newInstance();
//获取指定的某个方法
//参数一指明获取方法的名称 参数二指明获取的方法的形参列表
Method setS_id = clazz.getDeclaredMethod("setS_id", String.class);
//保证方法可访问
setS_id.setAccessible(true);
//调用指定方法
//参数一方法的调用对象 参数二给方法形参赋值的实参
setS_id.invoke(s,"1001");
System.out.println(s);
//调用静态方法的时候
名字.invoke(类名.class)或者.invoke(null)(其实填什么都无所谓,因为它都不会去识别);
调用指定的构造器
//获取Class对象
Class clazz = Class.forName("com.qianfeng.entity.Student");
//获取指定的构造器
//参数:构造器的参数列表如String.class....
Constructor declaredConstructor = clazz.getDeclaredConstructor();
//保证构造器可访问
declaredConstructor.setAccessible(true);
//调用构造器
Student s = (Student) declaredConstructor.newInstance();
5.2 注解
5.2.1注解的定义
注解就是源代码的元数据,通熟的讲就是代码中的标签
注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在
注解本身没有作用,在恰当的时候由外部程序进行解析才会发生作用
5.2.2 注解的分类
- 按来源分
- JDK 自带注解,例如:@Override, @Deprecated, @SuppressWornings 。
- 第三方注解
- 自定义注解
- 按生命周期划分
- SOURCE:只存在于源代码中,编译成 class 文件就不存在了
- Class:存在于源代码中和 class 文件中
- RUNTIME:注解保留到运行时
5.2.3 元注解
- @Retention:指明 Annotation 的生命周期,传入的值是一个枚举类型,可选值为:
• RetentionPolicy.SOURCE,
• RetentionPolicy.CLASS,
• RetentionPolicy.RUNTIME - @Target:指明 Annotation 可以修饰程序哪些元素,传入的值为ElemetType[] 类型,值可为:
• ElementType.CONSTRUCTOR :构造器
• ElementType.FIELD:属性
• ElementType.LOCAL_VARIABLE:局部变量
• ElementType.METHOD:方法
• ElementType.PACKAGE:包
• ElementType.PARAMETER:参数
• ElementType.TYPE:类、接口(包括注解类型和 enum 声明) - @Documented:
• 使用此修饰的注解将会被 javadoc 工具提取成文档,使用此注解,其 @Retention 必须被设置为 RetentionPolicy.RUNTIME 。 - @Inherited:
• 具有继承性。 - @Constraint
5.2.4 自定义注解
• 使用 @interface 关键字定义
• 自动继承 java.lang.annotation.Annotation 接口
• 配置参数的类型只能是八大基本类型、String、Class、enum、Annotation 和对应 的数组类型
• 配置参数声明的语法格式如下([] 表示可省略)类型 变量名() [default 默认值
• 如果只有一个配置参数,其参数名必须为 value
• 如果定义的注解含有配置参数,那在使用注解时,必须指定参数值,指定形式为:
“参数名=参数值”。如果只有一个参数,直接写参数值即可,定义中指定了默认 值的参数可以不指定值,但没有的一定要指定值
• 没有成员的注解为标记,包含成员的称为元数据
6. JDK8新特性
6.1 Lambda表达式
八大函数式接口
- Consumer : 接收一个参数,没有返回值,例如:list的遍历
- BiConsumer<T, E>: 接收两个参数,没有返回值,例如:Map的遍历
- Function<T, R>: 接收一个参数 T,返回一个值 R.
(UnaryOperator 接受一个对象,返回一个相同的对象, 继承与Function) - BiFunction<T, E, R>: 接收两个参数 T、E,返回一个值 R.
- BinaryOperator: 它继承了 BiFunction 接口,接收两个参数 T、T,返回一个值 T.
- Predicate: 谓词(断言),接口一个值 T, 返回一个boolean。
- Supplier: 没有参数,返回一个 T.
- Comparator: 比较,接收两个值 T, T, 返回 int类型。
6.2 Stream流
6.2.1 创建流
创建流的四种方式
//方式一:通过集合创建Stream流
List<String> list1 = new ArrayList<>();
list1.add("abc");
list1.add("def");
list1.add("ghi");
//集合的第一种方式.stream()方法获取Stream流
Stream<String> stream1 = list1.stream();
stream1.forEach(System.out::print);
System.out.println("\n*************");
//集合的第二种方式.parallelStream();(此种方式是通过多线程的方式)将一个大任务不断的拆分为很多的小任务,并压给不同的线程同时执行,最后将每个子任务的结果进行合并
Stream<String> stream2 = list1.parallelStream();
stream2.forEach(System.out::print);
System.out.println("\n**************");
//方式二:通过数组的来创建Stream流
int[] arr = new int[] {1,3,5,7,9};
IntStream stream3 = Arrays.stream(arr);
stream3.forEach(System.out::print);
System.out.println("\n**************");
//方式三:通过Stream流的静态方法of
Stream<Integer> stream4 = Stream.of(1,2,3,4,5);
stream4.forEach(System.out::print);
//方式四:通过.iterate()和.generate()获取无线流
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
6.2.2 流的中间操作
- filter(Predicate p):接受Lambda,从流中排出某些元素
- distinct():去除重复元素(通过hashCode与equals)
- limit(long maxSize):获取前指定个数元素
- skip(long n):跳过指定个数的元素
- sorted(Comparator c):按照自然排序,也可传入比较器
- map(Function f):接受一个函数做为参数,并将该函数应用到每个集合的每个元素上将其映射为一个新元素,一般用来直接提取信息或转换为其他形式的信息
6.2.3 流的终止操作
- allMatch(Predicate p): 检查集合中元素是否都匹配
- anyMatch(Predicate p): 检查集合中是否有一个匹配
- findFirst(): 返回第一个元素
- max(Comparator c): 获取最大值
- min(Comparator c): 获取最小值
- count(): 计数
- reduce(BinaryOperator b): 将流中的元素反复结合起来进行指定运算
- collect(): 接收一个Collector接口收集器参数并将流处理的结果收集到指定的容器中