Java复习笔记
最后更新时间:
索引
Java运行环境
JDK和JRE的作用
JRE(Java Runtime Environment)是Java程序的运行环境。包含JVM在%JRE安装目录%/bin/client/jvm.dll,所有的Java类库的class文件,在lib目录下打包成jar包。
JKD(Java Development Kit)是Java开发工具包。在%JDK安装目录%/bin/client/jvm.dll与在%JKD安装目录%/bin/server/jvm.dll下含有两个JVM虚拟机。同时只有JDK下才有javac。
配置JDK环境
- 我的电脑->属性->系统属性->高级->环境变量->系统环境变量,新建JAVA_HOME环境变量,值为 %jdk安装目录%,例:C:\Program Files\Java\jkd1.6.1_13
- 配置path环境变量:在path后加上;%JAVA_HOME%\bin
- 编译java代码:
1 | |
保存为HelloWorld.java,命令行进入当前目录使用命令:javac HelloWorld.java编译上传HelloWorld.class文件(class文件是字节码文件,字节码需要在JVM虚拟机里面运行)
- 使用命令java HelloWorld 运行
如果类指定了包名则需用命令javac -d HelloWorld.java来进行编译
.class文件
.class文件是字节码文件,字节码需要在JVM虚拟机里面运行。对一个.java文件进行编译可以产生一个同名的.class文件。若对一个含有内部类的.java文件进行编译会产生 外部类名$内部类名.class的文件。
类的加载机制
Java提供两种类的装载方式:
- 预先装载
- 按需装载(大部分类延迟到使用时才动态加载被称为Java的运行时动态装载机制)
Java的运行时动态装载机制:使得Java可以在动态运行时装载软件部件,修改代码无需全盘编译,为软件系统的开发提供了极大的灵活性。
- 预先装载
当启动一个程序时:Java首先在JDK目录下找到并载入jvm.dll->启动虚拟机->虚拟机进行初始化操作(设置操作系统参数等)->创建Bootstrap Loader对象(启动类装载器,由C++编写)->Bootstrap Loader一次性加载JVM的所有基础类
Bootstrap Loader还会装载定义在sun.misc命名空间下的Launcher类,Launcher类拥有两个内部类:ExtClassLoader, AppClassLoader. 其继承关系如下:
Bootstrap Loader<-ExtClassLoader<-AppClassLoader(拥有main()函数的入口类)
- 按需装载
装载条件:
1)静态方法
2)静态属性
3)构造方法
除外:
1)当访问静态常量属性时,JVM加载类的过程中不会进行类的初始化工作,只会进行到解析阶段
2)构造方法没有被显示的声明为静态方法,但它仍作为内地 静态成员特例
按需装载流程:
当需要使用一个类时JVM会检查这个类的Class对象是否已经加载,若未加载则开始装载:
- 加载:查找并导入类的二进制字节码文件,根据这些字节码文件创建一个Class对象
- 链接:分为校验、准备、解析
- 校验:检查导入的二进制字节码的完整性、正确性、安全性
- 准备:为静态域分配存储空间
- 解析:将符号引用转折为直接引用
- 初始化:初始化静态变量并执行静态域代码
类加载器:JVM使用类加载器来加载类,Java加载器在Java核心类库和CLASSPATH环境下面的所有类中查找类,若找不到则会抛出java.lang.ClassNotFoundException异常
从J2SE1.2开始:JVM使用了3中加载器:bootstrap、extension、system类加载器,依次为父子关系。
环境变量CLASSPATH的作用
环境变量CLASSPATH是在编译Java源码和运行程序时使用的,用于为Java程序指定所依赖的接口、类等搜索路径。
1 | |
如何为Java程序动态的指定类搜索路径
使用-cp选项,此时JVM会把指定的jar文件作为CLASSPATH的一部分
1 | |
如何使用cmd把Java程序打包成jar文件
jar包一般包含class文件、配置文件和清单文件manifest.mf
格式如下:
1 | |
1 | |
Java Web项目生成(Build)、部署(Deploy)、配置(Configuration)
目录结构:
1 | |
web.xml是整个Web应用程序的配置文件,通过它来定义Servlet、过滤器、监听器等,Web容器通过该文件的配置来控制整个Web应用程序的行为方式。须放在WEB-INF目录下
Servlet是服务器端处理HTTP请求的基本组成单元。JSP、过滤器都由其实现。Servlet存活在Web容器中,由Web容器来控制其生命周期。
JSP的脚本语言是Java,其本质是Servlet。
打包出来的文件为.war后缀,Java Web容器是符合Java EE规范的,所以每个Java Web应用程序都可以部署到任何平台的任何Java EE容器中。
Java语言
Java与C++程序的区别
C、C++:由编译器把源码直接编译成计算机可识别的机器码(exe、dll等),再直接运行。
Java:由javac命令把源文件编译成class文件,在Java程序启动时先启动Java虚拟机再由虚拟机去加载class文件。
简述JCM及其工作原理
JVM是一种用软件模拟出来的计算机,它用于执行Java程序,有一套非常严格的技术规范,是Java程序实现跨平台特性的基础。Java虚拟机有虚拟出来的计算机硬件如:处理器、寄存器、堆栈等,还具有与之配套的指令系统。它运行Java程序就行普通计算机运行C、C++程序一样。
Java程序为什么无需delete语句进行内存回收(JVM的垃圾回收机制)
JVM把程序创建的对象存放在堆空间中
堆(Heap):是一个运行时的数据存储区。分配和释放由程序中显示分配,没有垃圾自动回收机制,且须由程序代码显示释放这些实体。类似于C中的malloc()和free()。JVM会把程序创建的对象放在堆中,在Java中则由JVM自动释放(一般是垃圾回收器检测出一个对象不再被引用就就行回收)。
栈(Stack):一般存放非static的自动变量、函数参数、表达式的临时结果和函数返回值。分配和释放均由系统自动完成。
Java语法
变量及其作用域
全局变量:可以被所有函数在任何地址使用的变量
局部变量:在某一特定的代码范围才能看见的变量
根据生存周期来分:
- 静态变量:类中由static修饰的变量,当类加载时就生成并初始化
- 成员变量:类中没有使用static修饰的变量,当对象加载时就生成并初始化,随着垃圾回收器回收而消失
- 局部变量:定义在方法中的变量或方法的参数或定义在代码块中(用大括号包括的)的变量
Java变量数据类型
分为:
基本数据类型和引用数据类型
Java包含哪些基本数据类型及其包装类
基本数据类型:byte, short, int, long, float, double, boolean, char
包装类:Byte, Short, Integer, Long, Float, Double, Boolean, Character
int取值范围:int长度为4字节,共4*8=32位,第一位为符号位,最大值为2^31-1,最小值为-2^31
1 | |
long取值范围:长度为8字节,共8*8=64位,[-2^63, 2^63-1]
float取值范围:长度为4字节,共4*8=32位,[3.4E+10^-38, 3.4E+10^38]
double取值范围:长度为8字节,共8*8=64位,[1.7E+10^-308, 1.7E+10^308]
类型转换:分为显示转换和隐式转换
boolean存于栈空间,Boolean对象存放在堆空间中
char采用Unicode编码,用2字节表示一个字符,char长度为2字节,16位,[0, 2^16-1]
JVM启动时会实例化9个对象池,分别用来存储8种基本类型和String对象:对象池的作用是为了避免频繁的创建和销毁对象影响系统性能
StringBuffer线程安全,StringBuilder线程不安全。
使用指定的字符集创建String对象:String str = new String(“中午”.getBytes(), “GBK”),可用”GBK”, “UTF-8”, “ISO-8859-1”
装箱与拆箱
Java5.0提供的功能,用于打包基本数据类型,同时隐藏一些细节。自动装箱与拆箱是在编译阶段进行的。
转义字符
1 | |
Java的引用与C++的指针的区别
相同:都是指向一块内存地址的,通过引用指针来完成对内存数据的操作。
区别:
- 类型:引用的值为地址的数据元素,Java封装了的地址,可以转成字符查看,不必关心长度。C++指针是一个装地址的变量。
- 所占内存:引用声明没有实体,不占空间,C++指针用到才会赋值
- 初始值:java初始值为null,C++为原内存里所保存的值
- 计算:引用不可计算,C++相当于int可计算
- 控制:引用不可控制,C++可以使用计算来控制指针指向
- 内存泄漏:java不会,C++容易产生内存泄漏
Java中的main()方法
1 | |
作为程序的入口函数,可以通过args接受外部参数。
equal与==
==为直接比较值,若为基本数据类型则比较是否相同,若为引用则比较引用是否指向同一个对象。
equal则是调用java.lang.Object里的equal()方法或对象里面重写的equal方法来比较
Java中的三元运算符
1 | |
注释
1 | |
静态成员的特点
在类中通过static关键字修饰,包括:静态成员变量、静态方法、静态代码块
- 在类加载的时候就进行创建、初始化或执行代码
- 一个类只有一个
- 类的所有实例都可以访问
子类构造方法调用父类的构造方法
使用super()方法,且super()方法必须放在子类构造方法的第一行,若super()无参数则可省略
接口和抽象类的区别
抽象类是功能不全的类,里面可以有非抽象方法
接口是抽象方法声明和静态不能被修改数据的集合
两者都不能被实例化
一个类一次只能继承一个抽象类但可以实现多个接口
内部类
1 | |
B类的全类名是abc.A.B,且B会依赖于A。
下面分类讨论:
根据定义结构分类:
- 成员式:定义的方法与成员变量相似
- 局部式:定义在方法体重
成员内部类:
- 静态内部类:使用static关键字修饰的内部类,当加载外部类的时候也会加载静态内部类。无法访问外部类的非静态成员。全类名:abc.A.B,class文件名:A$B.class
1 | |
- 成员内部类:需要等外部类创建对象以后才会被加载到JVM中,属于外部类的某个实例,可访问外部类的静态与非静态成员。
1 | |
创建成员内部类语法:
1 | |
局部式内部类:
- 普通局部内部类:位于方法中
- 匿名内部类:没有类名,匿名内部类的class文件命名方法按照匿名内部类的排列顺序来进行:Outter$1.class
可见性private, protected, public, default
public:可被所有其它类访问
private:自身所在类内可见
protected:自身,子类及同一个包中类可访问
default:自身,同一个包中类可访问
Java数组
Java数组的本质
Java数组的本质是一个特殊的类,该类好保存了数据类型的信息。该类通过成员变量的形式保存数据,并通过[]符号来访问数据。基本数据类型的数组保存的是值(初始化为0),而应用类型的数组保存的是对象的引用(初始化为null)。
拷贝数组的数据
通过for遍历来赋值只是复制了对象的引用,若需要复制对象则可用:
1 | |
–
集合框架
列表List:有序,允许重复
集合Set:无序,不允许重复
SortedSet:有序的Set
映射Map:无序,不允许重复,键值对
SortedMap:有序的Map
迭代器
迭代器(Iterator)模式,又叫游标(Cursor)模式。提供一种方法来访问一个容器对象中的各个元素。
比较器
用于比较元素,需要实现Comparable或Comparator接口。
- Comparable接口:进行比较类需要实现的接口,仅包含一个compareTo()方法,返回值大于0时表示本对象<参数对象
1 | |
- Comparator接口:实现该接口的类被称为比较器,包含compare()方法。
1 | |
Vector 与 ArrayList 的区别
Vector是线程安全的,它操作元素的方法都是同步方法。ArrayList不是,但效率更高。
HashMap 与 HashTable 的区别
HashTable的方法是同步的,HashMap不同步
HashTable不允许null,HashMap允许null
HashTable使用Enumeration遍历,HashMap使用Iterator遍历
HashTable直接使用对象的hashCode,HashMap会重新计算
集合使用泛型
可以明确集合里存储的元素的类型,避免了手动类型转换的过程
集合元素排序
使用java.util.Collections类中的sort()方法对List元素进行排序
如果类中的元素全部实现了Comparable接口则可通过Collections.sort()排序
1 | |
若没有实现Comparable接口,也可以提供比较器来进行排序
什么集合可以使用 foreach
foreach运行步骤如下:
- 调用指定集合对象的Iterator()方法,得到迭代器
- 使用迭代器的hasNext()方法判断有无下一个元素进行循环
- 每次循环都用next()方法得到元素
数组或实现了Iterable接口的类实例,Jav集合框架中的集合大多符合第二条
Java I/O 输入/输出流
复制文件程序
1 | |
如果不关闭流,会造成资源的浪费,还可能会导致文件锁住,其他程序无法操作文件。
字节流
字节流处理的是最基本的单位byte,它可以处理任何形式的数据,主要操作byte数组。Java中可以使用java.io.FileInputStream和java.io.FileOutputStream来进行字节流的处理。
你也可以使用包装过的具有特定功能的字节流,基本使用思路如下:
- 获取输入或输出的流对象,从File获得或网络等
- 根据特定的字符格式创建InputStreamReader或InputStreamWriter
- 使用read()或readLine()方法读取数据,write()或print()
- 关闭流
序列化
把对象内存中的数据按照规则变成一系列的字节数据并写入到流中。须Serializable,必要时还需提供serialVersionUID
多线程
线程(Thread)与进程(Process)的区别
进程包含线程,每个应用程序的执行都在操作系统内核中登记一个进程标志,操作系统根据分配的标志对应用程序的执行进行调度和系统资源分配。进程是占用系统资源的基本单位。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存。
进程拥有固定的入口、执行顺序、出口,而线程会被应用程序控制。
并发(Concurrent)与并行(Parallel)的区别
并发:交替使用一台咖啡机;并行同时使用两台咖啡机。
并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。并行是并发的子集。
在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
让一个类成为线程类
- 实现Runnable接口
- 继承Thread类
继承Thread类之后就不能继承其它类了,实现Runnable接口则可以。
继承Thread类更方便。
实现Runnable接口的线程类更方便访问同一个变量,而Thread则需要使用内部类。
继承自Thread类可以通过new创建对象再调用start()方法。
实现Runnable接口的线程类需要将其对象作为Thread构造方法的参数,然后调用Thread对象的start()方法
使用sychronized让线程同步
每个对象都可以有一个线程锁,sychronized可以用任何一个对象的线程锁来锁住一段代码,任何想要进入该段代码的线程必须在解锁以后才能继续执行,否则进入等待状态。只有占用锁资源的线程执行完毕后,锁资源才会被释放。
java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronized methods )被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。
当一个线程调用一个对象的同步方法时,JVM会检查该对象的monitor。如果monitor没有被占用,那么这个线程就得到了monitor的占有 权,可以继续执行该对象的同步方法;如果monitor被其他线程所占用,那么该线程将被挂起,直到monitor被释放。
当线程退出同步方法调用时,该线程会释放monitor,这将允许其他等待的线程获得monitor以使对同步方法的调用执行下去。
编写一个生产者消费者模型的多线程例子
每个生产者在添加货物之前检查仓库是否已满,若已满则等待并通知消费者进行消费,直到消费者消费了至少一个货物以后再继续添加;消费者在消费一个货物之前检查仓库是否为空,若为空着等待并通知生产者进行生产,直到生产者添加了至少一个货物后,再进行消费。
sleep()函数是Thread类的静态函数,不涉及到线程间同步概念,仅仅为了让一个线程自身获得一段沉睡时间。sleep可以在任何地方使用。
wait函数是object类的函数,要解决的问题是线程间的同步,该过程包含了同步锁的获取和释放,调用wait方法将会将调用者的线程挂起,直到其他线程调用同一个对象的notify方法才会重新激活调用者。
1 | |
如何使用Java线程池
线程池属于对象池,其目的在于最大限度的复用对象。还可以使线程代码与业务代码分离。
1 | |
一个Runnable类型的对象通过execute(Runnable)方法添加到线程池。
当一个任务通过execute(Runnable)方法添加到线程池时:
- 如果此时线程池中线程数<corePoolSize,即使线程池中的线程都处于空闲状态也要添加新线程来处理任务
- 如果此时线程池中线程数=corePoolSize,但缓冲队列workQueue未满,那么任务被放入缓冲队列
- 如果此时线程池中线程数>corePoolSize,缓冲队列workQueue满,但线程池中线程数<maximumPoolSize,创建新线程来处理任务
- 如果此时线程池中线程数>corePoolSize,缓冲队列workQueue满,但线程池中线程数=maximumPoolSize,通过handler所指定的策略来处理此任务
1 | |
反射
反射原理
反射能够动态的加载一个类,动态的调用一个方法,动态的访问一个属性。JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后在通过java.lang.reflect包下的API来进行操作。
类型信息的存储
如果Java类文件存在内部类,那么编译这个文件时就会产生多个.class文件,命名规则为:外部类名$内部类名.class
例如:
1 | |
会产生 Perlon.class, Person$Tool.class, Person$Communitcation.class 三个文件
.class 文件结构
|
类型 |
名称 |
数量 |
长度 |
备注 |
|
u4 |
magic |
1 |
4Byte |
魔数:0xCAFEBABEOd -x命令可以看到,保证虚拟机可以轻松分辨Java文件和非Java文件。 |
|
u2 |
minor_version |
1 |
2Byte |
主版本号,class文件格式变化而变化 |
|
u2 |
major_version |
1 |
2Byte |
主版本号,class文件格式变化而变化 |
|
u2 |
constant_pool_count |
1 |
? |
常量个数 |
|
cp_info |
constant_pool |
constant_pool_count-1 |
? |
常量池:包含文件中类和接口相关常量。文字字符串、final变量值、类名和方法名的常量。通常占整个类大小的60% |
|
u2 |
access_flags |
1 |
2Byte |
访问标志:定义了类或接口。 |
|
u2 |
this_class |
1 |
2Byte |
常量池索引,指向常量池中该类全限定名的常量池入口 |
|
u2 |
super_class |
1 |
2Byte |
指向父类全限定名 |
|
u2 |
interfaces_count |
1 |
? |
该类实现的接口数量 |
|
u2 |
interfaces |
interfaces_count |
? |
由该类实现的接口的常量池引用 |
|
u2 |
fields_count |
1 |
? |
字段数量 |
|
field_info |
fields |
fields_count |
? |
字段信息表,描述字段的类型、描述符等 |
|
u2 |
methods_count |
1 |
? |
方法数量 |
|
method_info |
methods |
methods_count |
? |
方法本身,每个方法都有一个method_info表,记录了方法的方法名、字段类型、描述符等 |
|
u2 |
attributes_count |
1 |
? |
属性数量 |
|
attribute_info |
attributes |
attributes_count |
? |
属性本身 |
代理
静态代理
1 | |
动态代理
1 | |
Java网络编程
TCP/IP协议
TCP/IP(Transmission Control Protocel/Internet Protocol),传输控制协议/因特网互联协议,网络通讯协议。由网络层的IP协议与传输层的TCP协议组成。
- 应用层(Application Layer)
- 传输层(Transport Layer)
- 网络层(Network Layer)
- 链接层(Link Layer)
- 物理层(Physical Layer)
早期的时候,每家公司都有自己的电信号分组方式。逐渐地,一种叫做”以太网”(Ethernet)的协议,占据了主导地位。
以太网规定,一组电信号构成一个数据包,叫做”帧”(Frame)。每一帧分成两个部分:包头(Head)和数据(Data)。
“传输层”的功能,就是建立”端口到端口”的通信。相比之下,”网络层”的功能是建立”主机到主机”的通信。只要确定主机和端口,我们就能实现程序之间的交流。
Java Web 开发相关技术
1 | |
用户发送HTTP请求,Web容器通过http://<域名或IP地址>/<应用的名字>/<资源的地址>去定位资源
1 | |
算法题
反转字符串输出
1 | |
求素数
1 | |
改进版
1 | |
打印回文数字
1 | |
冒泡排序 BubbleSort
1 | |
插入排序 InsertSort
1 | |
快速排序 QuickSort
1 | |
归并排序
1 | |