Java基础知识 廖雪峰教程笔记

本文是关于Java基础知识的详细笔记,涵盖了从Java简介、运行Java程序到异常处理、反射、泛型、集合、多线程等多个核心概念。文章特别强调了反射在Java中的作用,动态代理的实现,以及模块化、泛型的擦拭法和泛型的边界。此外,还讨论了Java集合的使用,包括List、Map和Queue的实现,以及线程同步、死锁、锁的使用,如ReentrantLock、Condition和StampedLock。最后,提到了Spring框架中的IoC容器和AOP支持。
摘要由CSDN通过智能技术生成

Java基础知识

廖雪峰Java教程笔记。廖雪峰Java教程链接

java简介

java介于编译型语言和解释型语言之间。Java将代码编译成一种“字节码”,类似于抽象的CPU指令,针对不同平台编写虚拟机,不同平台的虚拟机负责加载字节码执行,这样,对Java开发者而言,就实现了 “一次编写,到处执行” 的效果。
对于虚拟机,需要对每个平台分别开发。

名词解释

  • JDK:Java Development Kit
  • JRE:Java Runtime Environment
    简单来说,JRE就是运行Java字节码的虚拟机。但是,如果只有Java源码,要编译成Java字节码,就需要JDK,因为JDK除了包含JRE,还提供了编译器、调试器等开发工具。
    两者关系如下:
    JDK与JRE的关系
  • JSR规范:Java Specification Request
  • JCP组织:Java Community Process
    为了保证Java语言的规范性,SUN公司搞了一个JSR规范,凡是想给Java平台加一个功能,比如说访问数据库的功能,大家要先创建一个JSR规范,定义好接口,这样,各个数据库厂商都按照规范写出Java驱动程序,开发者就不用担心自己写的数据库代码在MySQL上能跑,却不能跑在PostgreSQL上。

所以JSR是一系列的规范,从JVM的内存模型到Web程序接口,全部都标准化了。而负责审核JSR的组织就是JCP。

一个JSR规范发布时,为了让大家有个参考,还要同时发布一个“参考实现”,以及一个“兼容性测试套件”:

  • RI:Reference Implementation
  • TCK:Technology Compatibility Kit

运行Java程序

Java源码本质上是一个文本文件,先用javacHello.java编译成字节码文件Hello.class,然后,用java命令执行这个字节码文件:

流程图
可执行文件javac是编译器,可执行文件java是虚拟机。

Java基本数据类型

Java基本数据类型占用的字节数:
在这里插入图片描述
byte恰好是一个字节,longdouble需要8个字节。

模块

廖雪峰模块教程链接
从Java 9开始,原有的Java标准库已由一个单一巨大的rt.jar拆分成了几十个模块,这些模块以.jmod扩展名标识。模块之间的依赖关系已经被写入到模块内的module-info.class中了。
模块可以用来打包JRE。

包装类型

廖雪峰链接
Java对每种基本类型都提供了对应的引用类型。
在这里插入图片描述

记录类

record关键字定义记录类,方便定义不变类。
具体内容

异常处理

Java异常

Java异常继承关系图
Java规定:

  • 必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。
  • 不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

调用printStackTrace可以打印异常的传播栈,对于调试非常有用。

Java标准库中常用的异常类型有:
在这里插入图片描述

使用Commons Logging

和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块。

Commons Logging的特色是,它可以挂接不同的日志系统,并通过配置文件指定挂接的日志系统。默认情况下,Commons Loggin自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging。
使用Commons Logging只需要和两个类打交道,并且只有两步:

第一步,通过LogFactory获取Log类的实例; 第二步,使用Log实例的方法打日志。

使用log4j

Commons Logging作为“日志接口”来使用,而真正“日志实现”可以使用log4j。

反射

反射是为了解决在运行期间,对某个实例一无所知的情况下,如何调用其方法。

JVM为每个加载的class及interface创建了对应的Class实例来保存class及interface的所有信息;

获取一个class对应的Class实例后,就可以获取该class的所有信息;

通过Class实例获取class信息的方法称为反射(Reflection);

JVM总是动态加载class,可以在运行期根据条件来控制加载class。

动态代理

不编写实现类,在运行期间直接创建某个interface的实例,可以用动态代理来实现。
动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。

泛型

泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>

擦拭法

Java语言的泛型实现采用的是擦拭法,也就是说编译器完成工作,虚拟机对此一无所知。编译器内部永远把<T>视为Object类型,但在需要转型的时候,编译器会根据T的类型自动实现安全的强制转型。
Java泛型的局限:

  1. <T>不能是基本类型,因为实际的类型是Object,Object类型无法持有基本类型。
  2. 无法获取带泛型的Class。
  3. 无法判断带泛型的类型。
  4. 不能实例化T类型。

extends & super

对比extends和super通配符

我们再回顾一下extends通配符。作为方法参数, extends 类型和 super 类型的区别在于:

extends 允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);

super 允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。

一个是允许读不允许写,另一个是允许写不允许读。

集合

Java集合的设计有几个特点:一是实现了接口和实现类分离,例如,有序表的接口为List,具体的实现类有ArrayList,LinkedList等;二是支持泛型,可以限制在一个集合中只放入相同类型的元素;最后,Java访问集合总是通过统一的方式——迭代器实现的,它的好处在于无需知道集合内部的元素是按什么方式存储的。
由于Java的集合设计非常久远,中间经历过大规模改进,我们要注意到有一小部分集合类是遗留类,不应该继续使用:

  • Hashtable:一种线程安全的Map实现;
  • Vector:一种线程安全的List实现;
  • Stack:基于Vector实现的LIFO的栈。 还有一小部分接口是遗留接口,也不应该继续使用:
  • Enumeration:已被Iterator取代。

使用List

遍历List
遍历LinkedList不推荐用for循环+get(int),推荐使用iterator。

List<String> list = List.of("apple", "pear", "banana");
for(Iterator<String> it = list.iterator(); it.hasNext();){
   
	String s = it.next();
}

使用Map

编写equals()和hashCode()

要正确使用HashMap,作为key的类必须正确覆写equals()和hashCode()方法;

一个类如果覆写了equals(),就必须覆写hashCode(),并且覆写规则是:

如果equals()返回true,则hashCode()返回值必须相等;

如果equals()返回false,则hashCode()返回值尽量不要相等。

实现hashCode()方法可以通过Objects.hashCode()辅助方法实现。

  • equals():引用类型使用Objects.equals()比较,基本类型用==
  • hashCode():借助Objects.hash()
    编写equals()和hashCode()遵循的原则是:

equals()用到的用于比较的每一个字段,都必须在hashCode()中用于计算;equals()中没有使用到的字段,绝不可放在hashCode()中计算。

使用enumMap()

因为HashMap是一种通过对key计算hashCode(),通过空间换时间的方式,直接定位到value所在的内部数组的索引,效率很高。
如果作为key的对象是enum类型,那么,还可以使用Java集合库提供的一种EnumMap,它在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。

使用TreeMap

HashMap是一种空间换时间的映射表,它的实现原理决定了内部的key是无序的,即遍历HashMap的Key时,其顺序是不可预测的。
还有一种Map,它会在内部对Key进行排序,这种Map就是SortedMap。SortedMap只是接口,实现类时TreeMap。
在这里插入图片描述
使用TreeMap时,放入的Key必须实现Comparable接口。String、Integer这些类已经实现了Comparable接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。

如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>(){
   
	public int compare(Person p1, Person p2){
   
		return p1.name.compareTo(p2.name);
	}
})

Person类不用覆写equals和hashCode,因为TreeMap不使用它们。

使用Queue

在这里插入图片描述
LinkedList即实现了List接口,又实现了Queue接口

PriorityQueue

放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。

Deque

Deque是一个接口,它的实现类有ArrayDeque和LinkedList。

Stack

在Java中,我们用Deque可以实现Stack的功能:

  • 把元素压栈:push(E)/addFirst(E);
  • 把栈顶的元素“弹出”:pop(E)/removeFirst();
  • 取栈顶元素但不弹出:peek(E)/peekFirst()。
    当我们把Deque作为Stack使用时,注意只调用push()/pop()/peek()方法,不要调用addFirst()/removeFirst()/peekFirst()方法,这样代码更加清晰。

IO

InputStream/OutputStream

IO流以byte(字节)为最小单位,因此也称为字节流。

Reader/Writer

如果需要读写的是字符,并且字符不全是单字节表示的ASCII字符,使用char来读写更加方便,这种称为字符流。
Java提供了Reader和Writer两种字符流,传输的最小单位是char。
Reader和Writer本质上是一个能自动编解码的InputStream和OutputStream。

同步和异步

同步IO是指,读写IO时代码必须等待数据返回后才继续执行后续代码,它的优点是代码编写简单,缺点是CPU执行效率低。

而异步IO是指,读写IO时仅发出请求,然后立刻执行后续代码,它的优点是CPU执行效率高,缺点是代码编写复杂。

Java标准库的包java.io提供了同步IO,而java.nio则是异步IO。上面我们讨论的InputStream、OutputStream、Reader和Writer都是同步IO的抽象类,对应的具体实现类,以文件为例,有FileInputStream、FileOutputStream、FileReader和FileWriter。

Filter

Java的IO标准库使用Filter模式为InputStream和OutputStream增加功能:

可以把一个InputStream和任意个FilterInputStream组合;

可以把一个OutputStream和任意个FilterOutputStream组合。

Filter模式可以在运行期动态增加功能(又称Decorator模式&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值