JAVA的学习(下)
2019版尚硅谷Java入门视频教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kb411W75N?p=5
十一、Java集合
11-1 Java集合概述
1.集合的使用场景
Java 集合可分为 Collection 和 Map 两种体系
ØCollection接口:单列数据
,定义了存取一组对象的方法的集合
List:元素有序、可重复的集合
Set:元素无序、不可重复的集合
Ø Map接口:双列数据
,保存具有映射关系key-value对
的集合
2.Collection接口继承树
3.Map接口继承树
11-2 Collection接口方法
l Collection 接口是 List、Set 和 Queue 接口
的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
l JDK不提供
此接口的任何直接实现,而是提供更具体的子接口(如:Set和List
)实现
l 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object
类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型
。
11-3 Iterator迭代器接口
1.使用 Iterator 接口遍历集合元素
2.Iterator接口的方法
3.迭代器的执行原理
4.Iterator接口remove()方法
5.使用foreach循环遍历集合元素
11-4 Collection子接口之一:List接口
1.List接口概述
2.List接口方法
List实现类之一:ArrayList
List实现类之二:LinkedList
对于频繁的插入或删除元素的操作,建议使用LinkedList
类,效率较高
新增方法:
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()
List 实现类之三:Vector
| Vector 是一个古老的集合,JDK1.0
就有了。大多数操作与ArrayList
相同,区别之处在于Vector是线程安全的
。
| 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList
;Vector总是比ArrayList慢,所以尽量避免使用。
新增方法:
void addElement(Object obj)
void insertElementAt(Object obj,int index)
void setElementAt(Object obj,int index)
void removeElement(Object obj)
void removeAllElements()
ArrayList、LinkedList、Vector的异同
请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层是什么?
扩容机制?Vector和ArrayList的最大区别?
l ArrayList和LinkedList的异同
二者都线程不安全,相对线程安全的Vector,执行效率高。
此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
l ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。
因此开销就比ArrayList要大,访问要慢。
正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
11-5 Collection子接口之二:Set接口
1.Set 接口概述
Set接口是Collection
的子接口,set接口没有提供额外的方法
Set 集合不允许包含相同的元素
,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用==
运算符,而是根据 equals
方法
2.Set实现类之一:HashSet
HashSet
是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法
来存储集合中的元素,因此具有很好的存取、查找和删除性能。
HashSet 具有以下特点:
Ø 不能保证元素的排列顺序
Ø HashSet 不是线程安全的
Ø 集合元素可以是 null
l HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode()
方法比较相等,并且两个对象的 equals()
方法返回值也相等。
l 对于存放在Set容器中的对象,对应的类一定要重写**equals()和hashCode(Object obj)**方法,以实现对象相等规则。即:相等的对象必须具有相等的散列码
。
重写hashCode()方法的基本原则
在程序运行时,同一个对象多次调用 hashCode()
方法应该返回相同的值
当两个对象的 equals()
方法比较返回 true
时,这两个对象的 hashCode()
方法的返回值也应相等
对象中用作 equals()
方法比较的 Field
,都应该用来计算 hashCode
值
重写 equals() 方法的基本原则
Eclipse、IDEA工具里hashCode()的重写
3.Set实现类之二:LinkedHashSet
LinkedHashSet
是 HashSet 的子类
LinkedHashSet 根据元素的 hashCode
值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
LinkedHashSet 不允许集合元素重复。
LinkedHashSet底层结构
4.Set实现类之三:TreeSet
l TreeSet
是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态
。
l TreeSet底层使用红黑树
结构存储数据(详情http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html)
l 新增的方法如下: (了解)
l TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序
。
自然排序
定制排序
11-6 Map接口
1.Map接口继承树
2.Map接口概述
3.常用方法
1.Map实现类之一:HashMap
HashMap的存储结构
HashMap源码中的重要常量
HashMap的存储结构:JDK 1.8之前
HashMap的存储结构:JDK 1.8
面试题:负载因子值的大小,对HashMap有什么影响
l 负载因子的大小决定了HashMap
的数据密度。
l 负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降。
l 负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
l 按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75
,此时平均检索长度接近于常数。
2.Map实现类之二:LinkedHashMap
l LinkedHashMap
是 HashMap 的子类
l 在HashMap存储结构的基础上,使用了一对双向链表
来记录添加元素的顺序
l 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
HashMap中的内部类:Node 与 LinkedHashMap中的内部类:Entry
3.Map实现类之三:TreeMap
4.Map实现类之四:Hashtable
5.Map实现类之五:Properties
11-7 Collections工具类
操作数组的工具类
:Arrays
1.Collections常用方法
2.Collections常用方法:同步控制
l Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
3.补充:Enumeration
l Enumeration
接口是 Iterator 迭代器的 “古老版本”
十二、泛型(Generic)
12-1 为什么要有泛型
- 解决元素存储的安全性问题
- 解决获取数据元素时,需要类型强转的问题
泛型的概念
l 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型
或者是某个方法的返回值及参数类型
。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。
l 从JDK1.5以后,Java引入了参数化类型(Parameterized type)
的概念,允许我们在创建集合时再指定集合元素的类型,正如:List<String>
,这表明该List只能保存字符串类型的对象。
l JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
12-2 在集合中使用泛型
12-3 自定义泛型结构
自定义泛型结构:泛型类、泛型接口
自定义泛型结构:泛型方法
12-4 泛型在继承上的体现
如果B
是A
的一个子类型(子类或者子接口),而G
是具有泛型声明的类或接口,G<B>
并不是G<A>
的子类型!
比如:String
是Object
的子类,但是List<String >
并不是List<Object>
的子类。
public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父类.
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(报错)
}
12-5 通配符(?)的使用
通配符的使用:注意点
有限制的通配符
【注意点】
1.对象实例化时不指定泛型,默认为:Object
。
2.泛型不同的引用不能相互赋值。
3.加入集合中的对象类型必须与指定的泛型类型一致
。
4.静态方法中不能
使用类的泛型。
5.如果泛型类是一个接口或抽象类,则不可创建
泛型类的对象。
6.不能在catch
中使用泛型
7.从泛型类派生子类,泛型类型需具体化
12-6 泛型应用举例
泛型嵌套
public static void main(String[] args) {
HashMap<String, ArrayList<Citizen>> map = new HashMap<String, ArrayList<Citizen>>();
ArrayList<Citizen> list = new ArrayList<Citizen>();
list.add(new Citizen("刘恺威"));
list.add(new Citizen("杨幂"));
list.add(new Citizen("小糯米"));
map.put("刘恺威", list);
Set<Entry<String, ArrayList<Citizen>>> entrySet = map.entrySet();
Iterator<Entry<String, ArrayList<Citizen>>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Entry<String, ArrayList<Citizen>> entry = iterator.next();
String key = entry.getKey();
ArrayList<Citizen> value = entry.getValue();
System.out.println("户主:" + key);
System.out.println("家庭成员:" + value);
}
}
实际案例
//package com.atguigu.java2;
interface Info{ // 只有此接口的子类才是表示人的信息
}
class Contact implements Info{ // 表示联系方式
private String address ; // 联系地址
private String telephone ; // 联系方式
private String zipcode ; // 邮政编码
public Contact(String address,String telephone,String zipcode){
this.address = address;
this.telephone = telephone;
this.zipcode = zipcode;
}
public void setAddress(String address){
this.address = address ;
}
public void setTelephone(String telephone){
this.telephone = telephone ;
}
public void setZipcode(String zipcode){
this.zipcode = zipcode;
}
public String getAddress(){
return this.address ;
}
public String getTelephone(){
return this.telephone ;
}
public String getZipcode(){
return this.zipcode;
}
@Override
public String toString() {
return "Contact [address=" + address + ", telephone=" + telephone
+ ", zipcode=" + zipcode + "]";
}
}
class Introduction implements Info{
private String name ; // 姓名
private String sex ; // 性别
private int age ; // 年龄
public Introduction(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}
public void setName(String name){
this.name = name ;
}
public void setSex(String sex){
this.sex = sex ;
}
public void setAge(int age){
this.age = age ;
}
public String getName(){
return this.name ;
}
public String getSex(){
return this.sex ;
}
public int getAge(){
return this.age ;
}
@Override
public String toString() {
return "Introduction [name=" + name + ", sex=" + sex + ", age=" + age
+ "]";
}
}
class Person<T extends Info>{
private T info ;
public Person(T info){ // 通过构造器设置信息属性内容
this.info = info ;
}
public void setInfo(T info){
this.info = info ;
}
public T getInfo(){
return info ;
}
@Override
public String toString() {
return "Person [info=" + info + "]";
}
}
public class GenericPerson{
public static void main(String args[]){
Person<Contact> per = null ; // 声明Person对象
per = new Person<Contact>(new Contact("北京市","01088888888","102206")) ;
System.out.println(per);
Person<Introduction> per2 = null ; // 声明Person对象
per2 = new Person<Introduction>(new Introduction("李雷","男",24));
System.out.println(per2) ;
}
}
十三、IO流
13-1 File类的使用
l java.io.File
类:文件和目录路径的抽象表示形式,与平台无关
l File
能新建、删除、重命名文件和目录,但 File
不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流
。
l File
对象可以作为参数传递给流
的构造函数
l 想要在Java程序中表示一个真实存在的文件或目录,那么必须
有一个File对象,但是Java程序中的一个File对象,可能没有
一个真实存在的文件或目录。
1.File类的常见构造方法(构造器)
2.路径分隔符
3.常用方法
File dir1 = new File("D:/IOTest/dir1");
// 如果D:/IOTest/dir1不存在,就创建为目录
if (!dir1.exists()) {
dir1.mkdir(); }
// 创建以dir1为父目录,名为"dir2"的File对象
File dir2 = new File(dir1, "dir2");
if (!dir2.exists()) { // 如果还不存在,就创建为目录
dir2.mkdirs();
}
File dir4 = new File(dir1, "dir3/dir4");
if (!dir4.exists()) {
dir4.mkdirs();
}
// 创建以dir2为父目录,名为"test.txt"的File对象
File file = new File(dir2, "test.txt");
if (!file.exists()) { // 如果还不存在,就创建为文件
file.createNewFile();
}
结果如下:
13-2 IO流原理及流的分类
1.Java IO原理
l I/O
是Input/Output
的缩写, I/O
技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等
。
l IO流
用来处理设备之间的数据传输。
l Java程序中,对于数据的输入/输出操作以流(stream)
的方式进行。
l java.io
包下提供了各种流
类和接口
,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
2.流的分类
按操作数据单位不同分为:字节流(8 bit)
,字符流(16 bit)
按数据流的流向不同分为:输入流
,输出流
按流的角色的不同分为:节点流
,处理流
\1. Java的IO
流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类
派生的。
\2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
3.IO 流体系
4.节点流和处理流
5.输入流的基类:InputStream & Reader
InputStream
Reader
6.输出流的基类:OutputStream & Writer
OutputStream
Writer
13-3 节点流(或文件流)
1.读取文件
2.写入文件
3.注意点
l 定义文件路径时,注意:可以用/
或者\
。
l 在写入一个文件时,如果使用构造器FileOutputStream(file)
,则目录下有同名文件将被覆盖
。
l 如果使用构造器FileOutputStream(file,true)
,则目录下的同名文件不会被覆盖,在文件内容末尾追加
内容。
l 在读取文件时,必须保证该文件已存在,否则报异常。
l 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
l 字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp 等语言的源代码
。尤其注意.doc,.excel,.ppt
这些不是文本文件。
13-4 处理流之一:缓冲流
BufferedReader br = null;
BufferedWriter bw = null;
try {
//step1:创建缓冲流对象:它是过滤流,是对节点流的包装
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\destBF.txt"));
String str = null;
while ((str = br.readLine()) != null) { //一次读取字符文本文件的一行字符
bw.write(str); //一次写入一行字符串
bw.newLine(); //写入行分隔符
}
bw.flush(); //step2:刷新缓冲区
} catch (IOException e) {
e.printStackTrace();
} finally {
// step3: 关闭IO流对象
try {
if (bw != null) {
bw.close(); //关闭过滤流时,会自动关闭它所包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
13-5 处理流之二:转换流
1.InputStreamReader
2.OutputStreamWriter
3.补充:字符编码
编码表的由来
计算机只能识别二进制
数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表
。
常见的编码表
Ø ASCII
:美国标准信息交换码。用一个字节的7位表示。
Ø ISO8859-1
:拉丁码表。欧洲码表用一个字节的8位表示。
Ø GB2312
:中国的中文编码表。
Ø GBK
:中国的中文编码表升级,融合了更多的中文文字符号。
Ø Unicode
:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode
Ø UTF-8
:最多用三个字节来表示一个字符。
l Unicode
不完美,这里就有三个
问题,一个是
,我们已经知道,英文字母只用一个字节表示就够了,第二个问题
是如何才能区别Unicode和ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个
,如果和GBK等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。Unicode在很长一段时间内无法推广,直到互联网的出现。
l 面向传输的众多 UTF(UCS Transfer Format)
标准出现了,顾名思义,UTF-8
就是每次8个位传输数据,而UTF-16
就是每次16个位。这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
l Unicode只是定义了一个庞大的、全球通用的字符集
,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流
,取决于字符编码方案。推荐的Unicode编码是UTF-8
和UTF-16
。
编码:字符串---->字节数组
解码:字节数组--->字符串
转换流的编码应用:
Ø 可以将字符按指定编码格式存储。
Ø 可以对文本数据按指定编码格式来解读。
Ø 指定编码表的动作由构造器完成。
13-6 处理流之三:标准输入、输出流
System.in和System.out分别代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器
System.in
的类型是InputStream
System.out
的类型是PrintStream
,其是OutputStream
的子类FilterOutputStream
的子类,FilterOutputStream
的子类
l 重定向:通过System类的setIn,setOut方法对默认设备进行改变。
Ø public static void setIn(InputStream in)
Ø public static void setOut(PrintStream out)
System.out.println("请输入信息(退出输入e或exit):");
//把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { //读取用户输入的一行数据 --> 阻塞程序
if (s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")) {
System.out.println("安全退出!!");
break;
}
//将读取到的整行字符串转成大写输出
System.out.println("-->:"+s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); //关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
13-7 处理流之四:打印流(了解)
13-8 处理流之五:数据流(了解)
DataOutputStream dos = null;
try { //创建连接到指定文件的数据输出流对象
dos = new DataOutputStream(new FileOutputStream("d:\\IOTest\\destData.dat"));
dos.writeUTF("ab中国"); //写UTF字符串
dos.writeBoolean(false); //写入布尔值
dos.writeLong(1234567890L); //写入长整数
System.out.println("写文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { //关闭流对象
try {
if (dos != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
13-9 处理流之六:对象流
对象的序列化
使用对象流序列化对象
//序列化:将对象写入到磁盘或者进行网络传输。
//要求对象必须实现序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test3.txt"));
Person p = new Person("韩梅梅",18,"中华大街",new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:将磁盘中的对象数据源读出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test3.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();
谈谈你对java.io.Serializable接口的理解,我们知道它用于序列化,是空方法接口,还有其它认识吗?
l 实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新装配
。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
l 由于大部分作为参数的类如String、Integer等都实现了java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更灵活。
13-10 随机存取文件流
1.RandomAccessFile 类
我们可以用RandomAccessFile
这个类,来实现一个多线程断点下载
的功能,用过下载工具的朋友们都知道,下载前都会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载,从而实现断点下载或上传的功能,有兴趣的朋友们可以自己实现下。
2.读取文件内容
3.写入文件内容
13-11 流的基本应用小结
流
是用来处理数据的。
处理数据时,一定要先明确数据源,与数据目的地
Ø 数据源
可以是文件,可以是键盘。
Ø 数据目的地
可以是文件、显示器或者其他设备。
而流
只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理
等。
13-12 NIO.2中Path、Paths、Files类的使用
1.Java NIO 概述
NIO. 2
l 随着 JDK 7
的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2
。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分
。
2.Path、Paths和Files核心API
Path接口常用方法
Files 类
十四、网络编程
14-1 网络编程概述
l Java是Internet
上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
l Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由JVM
进行控制。并且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
网络基础
l 计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
l 网络编程的目的:
直接或间接地通过网络协议
与其它计算机实现数据交换,进行通讯。
l 网络编程中有两个主要的问题:
Ø 如何准确地定位网络上一台或多台主机
Ø 找到主机后如何可靠高效地进行数据传输。
14-2 网络通信要素概述
如何实现网络中的主机互相通信:
网络通信协议
14-3 通信要素1:IP和端口号
1.IP地址
2.端口号
3.InetAddress类
InetAdress 代码示例
14-4 通信要素2:网络通信协议
网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制
等制定标准。
问题:网络协议太复杂
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制
,如何实现如此复杂的网络协议呢?
通信协议分层的思想
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式
,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
1.TCP/IP协议簇
传输层协议中有两个非常重要的协议:
Ø 传输控制协议TCP(Transmission Control Protocol)
Ø 用户数据报协议UDP(User Datagram Protocol)
TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
IP(Internet Protocol)协议
是网络层的主要协议,支持网间互连的数据通信。
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层。
TCP 和 UDP
TCP协议:
Ø 使用TCP协议前,须先建立TCP连接,形成传输数据通道
Ø 传输前,采用“三次握手”方式,是可靠的
Ø TCP协议进行通信的两个应用进程:客户端、服务端
Ø 在连接中可进行大数据量的传输
Ø 传输完毕,需释放已建立的连接,效率低
UDP协议:
Ø 将数据、源、目的封装成数据包,不需要建立连接
Ø 每个数据报的大小限制在64K
内
Ø 因无需连接,故是不可靠的
Ø 发送数据结束时无需释放资源,开销小,速度快
TCP三次握手
TCP四次挥手
2.Socket
Socket类常用构造器及方法
14-5 TCP网络编程
基于Socket的TCP编程
客户端Socket的工作过程包含以下四个基本的步骤
Ø 创建 Socket:根据指定服务端的IP 地址
或端口号
构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
Ø 打开连接到 Socket 的输入/出流: 使用 getInputStream()
方法获得输入流,使用 getOutputStream()
方法获得输出流,进行数据传输
Ø 按照一定的协议对Socket
进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程。
Ø 关闭 Socket:断开客户端到服务器的连接,释放线路
客户端创建Socket对象
服务器程序的工作过程包含以下四个基本的步骤
Ø 调用 ServerSocket(int port)
:创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
Ø 调用 accept()
:监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
Ø 调用 该Socket
类对象的 getOutputStream()
和 getInputStream ()
:获取输出流和输入流,开始网络数据的发送和接收。
Ø 关闭ServerSocket
和Socket
对象:客户端访问结束,关闭通信套接字。
服务器建立ServerSocket对象
ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的ServerSocket对象。
所谓“接收”客户的套接字请求,就是accept()
方法会返回一个 Socket
对象
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
14-6 UDP网络编程
UDP网络通信
类 DatagramSocket
和 DatagramPacket
实现了基于UDP
协议网络程序。
UDP数据报通过数据报套接字 DatagramSocket
发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
DatagramPacket
对象封装了UDP数据报,在数据报中包含了发送端
的IP地址和端口号以及接收端
的IP地址和端口号。
UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接,如同发快递包裹一样。
DatagramSocket 类的常用方法
DatagramPacket类的常用方法
UDP网络通信流程:
1.DatagramSocket与DatagramPacket
2.建立发送端,接收端
3.建立数据包
4.调用Socket的发送、接收方法
5.关闭Socket
发送端与接收端是两个独立的运行程序
发送端
接收端
//发送端:
DatagramSocket ds = new DatagramSocket();
byte[] by = “hello,atguigu.com”.getBytes();
DatagramPacket dp = new DatagramPacket(by,0,by.length,
InetAddress.getByName(“127.0.0.1”),10000);
ds.send(dp);
ds.close();
//接收端
//在接收端,要指定监听的端口。
DatagramSocket ds = new DatagramSocket(10000);
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by,by.length);
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength());
System.out.println(str+"--"+dp.getAddress());
ds.close();
14-7 URL编程
1.URL类
URL类构造器
URL类常用方法
2.针对HTTP协议的URLConnection类
3.URL与URN与URI
URI,是uniform resource identifier
,统一资源标识符,用来唯一的标识一个资源。
URL,是uniform resource locator
,统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
URN,是uniform resource name
,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com
。
也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI
。
-
经验式理解:http://localhost:8080/myweb/hello.html
以上web地址,加粗部分+非加粗部分=URL;非加粗部分=URI。这是大部分程序员对两者理解的一个典型代表。 -
URI,是
uniform resource identifier
,统一资源标识符,用来唯一的标识一个资源。Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的URI一般由三部组成:①访问资源的命名机制②存放资源的主机名③资源自身的名称,由路径表示,着重强调于资源
。 -
URL(
Uniform Resource Locator
):统一资源定位符,顾名思义,URL就是一个表示资源位置的字符串,基本的URL格式为协议://IP地址/路径和文件名
,如:ftp://ftp.is.co.za/rfc/rfc1808.txt
-
最重要的一点,URL对于我们而言,就是将URL输入到浏览器地址栏上就可以访问到对应资源。
-
URN (
Uniform Resource Name
):统一资源命名,作为特定内容的唯一名称使用的,与当前资源的所在地无关。使用URN,就可以将资源四处迁移,而不用担心迁移后无法访问。P2P下载中使用的磁力链接是URN的一种实现
,它可以持久化的标识一个BT
资源,资源分布式的存储在P2P
网络中,无需中心服务器用户即可找到并下载它。 -
“A Uniform Resource Identifier (URI) 是一个紧凑的字符串用来标示抽象或物理资源”,可以看出其和URL的目的是相同的,都是通过使用字符串来标示资源,这样看来,像开头加粗部分的字符串似乎并不能完整标识资源。
-
“A Uniform Resource Name (URN)唯一标识一个实体的标识符”,但是不给出实体的位置,通过引用一个或多个URL来实现标识指针功能。现实中可见的使用:
P2P下载中使用的磁力链接
。 -
URL 和 URN 都是 URI 的子集
。
4.小结
l 位于网络中的计算机具有唯一的IP地址,这样不同的主机可以互相区分。
l 客户端-服务器是一种最常见的网络应用程序模型。服务器是一个为其客户端提供某种特定服务的硬件或软件
。客户机是一个用户应用程序,用于访问某台服务器提供的服务
。
l 端口号是对一个服务的访问场所,它用于区分同一物理计算机上的多个服务。
l 套接字用于连接客户端和服务器,客户端和服务器之间的每个通信会话使用一个不同的套接字。TCP协议用于实现面向连接的会话。
l Java 中有关网络方面的功能都定义在java.net
程序包中。Java 用InetAddress
对象表示 IP地址,该对象里有两个字段:主机名(String) 和 IP 地址(int)
。
l 类 Socket
和 ServerSocket
实现了基于TCP协议的客户端-服务器程序。Socket是客户端和服务器之间的一个连接,连接创建的细节被隐藏了。这个连接提供了一个安全的数据传输通道,这是因为 TCP 协议可以解决数据在传送过程中的丢失、损坏、重复、乱序以及网络拥挤等问题,它保证数据可靠的传送。
l 类 URL
和 URLConnection
提供了最高级网络应用。URL 的网络资源的位置来同一表示Internet 上各种网络资源。通过URL对象可以创建当前应用程序和 URL 表示的网络资源之间的连接,这样当前程序就可以读取网络资源数据,或者把自己的数据传送到网络上去。
十五、反射(Reflection)机制
15-1 Java反射机制概述
1.Java Reflection
2.补充:动态语言 vs 静态语言
3.Java反射机制研究及应用
l Java反射机制提供的功能
Ø 在运行时判断任意一个对象所属的类
Ø 在运行时构造任意一个类的对象
Ø 在运行时判断任意一个类所具有的成员变量和方法
Ø 在运行时获取泛型信息
Ø 在运行时调用任意一个对象的成员变量和方法
Ø 在运行时处理注解
Ø 生成动态代理
4.反射相关的主要API
l java.lang.Class:代表一个类
l **java.lang.reflect.Method:**代表类的方法
l **java.lang.reflect.Field:**代表类的成员变量
l **java.lang.reflect.Constructor:**代表类的构造器
l … …
15-2 理解Class类并获取Class的实例
1.Class 类
Class类的常用方法
2.实例化Class类对象(四种方法)
哪些类型可以有Class对象
15-3 类的加载与ClassLoader的理解
1.了解:类的加载过程
2.了解:什么时候会发生类初始化
l 类的主动引用(一定会发生类的初始化)
Ø 当虚拟机启动,先初始化main
方法所在的类
Ø new一个类的对象
Ø 调用类的静态成员(除了final常量
)和静态方法
Ø 使用java.lang.reflect
包的方法对类进行反射调用
Ø 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
l 类的被动引用(不会发生类的初始化)
Ø 当访问一个静态域时,只有真正声明这个域的类才会被初始化
当通过子类引用父类的静态变量,不会导致子类初始化
Ø 通过数组定义类引用,不会触发此类的初始化
Ø 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
3.类加载器的作用
l 类加载的作用:将class
文件字节码内容加载到内存中,并将这些静态数据转换成方法区
的运行时数据结构,然后在堆
中生成一个代表这个类的java.lang.Class
对象,作为方法区中类数据的访问入口。
l 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM
垃圾回收机制可以回收这些Class
对象。
4.了解:类加载器(ClassLoader)
**类加载器是用来把类(class)装载进内存的。**JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)
和用户自定义加载器(user-defined class loader)
。
JVM在运行时会产生3个类加载器
组成的初始化加载器层次结构 ,如下图所示:
15-4 创建运行时类的对象
有了Class对象,能做什么
15-5 获取运行时类的完整结构
1.通过反射获取运行时类的完整结构
Field(属性)、Method(方法)、Constructor(构造)、Superclass(父类)、Interface(接口)、Annotation(注解)
Ø 实现的全部接口
Ø 所继承的父类
Ø 全部的构造器
Ø 全部的方法
Ø 全部的Field
2.使用反射可以取得
3.小结
1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。
2.一定要熟悉java.lang.reflect
包的作用,反射机制。
3.如何取得属性、方法、构造器的名称,修饰符等。
`
15-6 调用运行时类的指定结构
1.调用指定方法
2.调用指定属性
3.关于setAccessible方法的使用
15-7 反射的应用:动态代理
1.Java动态代理相关API
proxy
2.动态代理步骤
3.动态代理与AOP(Aspect Orient Programming)
l 使用Proxy
生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理
l 这种动态代理在AOP中被称为AOP代理
,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
十六、Java8的其他新特性
Java 8新特性简介
Java 8 (又称为 jdk 1.8
) 是 Java 语言开发的一个主要版本。
Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
l 速度更快
l 代码更少(增加了新的语法:Lambda 表达式)
l 强大的 Stream API
l 便于并行
l 最大化减少空指针异常:Optional
l Nashorn引擎,允许在JVM
上运行JS
应用
并行流与串行流
并行流
就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8 中将并行
进行了优化,我们可以很容易的对数据进行并行操作。Stream API
可以声明性地通过 parallel()
与 sequential()
在并行流与顺序流之间进行切换。
16-1 Lambda表达式
为什么使用 Lambda 表达式
Lambda
是一个匿名函数
,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
从匿名类到 Lambda 的转换举例1
l 从匿名类到 Lambda 的转换举例2
Lambda 表达式:语法
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 ->
, 该操作符被称为 Lambda 操作符
或箭头操作符
。它将 Lambda 分为两个部分:
左侧
:指定了 Lambda 表达式需要的参数列表
右侧
:指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。
类型推断
16-2 函数式(Functional)接口
什么是函数式(Functional)接口
如何理解函数式接口
函数式接口举例
自定义函数式接口
1.作为参数传递 Lambda 表达式
2.Java 内置四大核心函数式接口
3.其他接口
16-3 方法引用与构造器引用
1.方法引用(Method References)
2.构造器引用
3.数组引用
16-4 强大的Stream API
1.Stream API说明
l Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
l Stream API ( java.util.stream)
把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API
可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
l Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 **使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。**也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
为什么要使用Stream API
l 实际开发中,项目中多数数据源都来自于Mysql,Oracle
等。但现在数据源可以更多了,有MongDB,Radis
等,而这些NoSQL
的数据就需要Java层面去处理。
l Stream
和 Collection
集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存
,存储在内存中,后者主要是面向 CPU
,通过 CPU 实现计算。
什么是 Stream
2.Stream 的操作三个步骤
创建 Stream方式一:通过集合
创建 Stream方式二:通过数组
创建 Stream方式三:通过Stream的of()
创建Stream方式四:创建无限流
3.Stream 的中间操作
4.Stream 的终止操作
5.强大的Stream API:Collectors
16-5 Optional类
十七、Java9&Java10&Java11 新特性
自从 2017 年 9 月 21 日 Java 9 正式发布之时,Oracle 就宣布今后会按照每六个月一次的节奏进行更新,在过去的几个月中,我们见证了其兑现了诺言,但万万没想到,苦了大批迎头而上的开发者们。
17-1 Java 9 的新特性
JDK 9 的发布
l 经过4次跳票,历经曲折的Java 9 终于终于在2017年9月21日发布。
l 从Java 9 这个版本开始,Java 的计划发布周期是 6 个月,下一个 Java 的主版本将于 2018 年 3 月发布,命名为 Java 18.3,紧接着再过六个月将发布 Java18.9。
l 这意味着Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的(6 个月为周期
)发布模式,并逐步的将 Oracle JDK
原商业特性进行开源。
l 针对企业客户的需求,Oracle 将以三年为周期
发布长期支持版本(long termsupport)。
l Java 9 提供了超过150项新功能特性,包括备受期待的模块化系统、可交互的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。可以说Java 9是一个庞大的系统工程,完全做了一个整体改变。
1.JDK 和 JRE 目录结构的改变
2.模块化系统: Jigsaw -> Modularity
3.Java的REPL工具:jShell命令
4.语法改进:接口的私有方法
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。
5.语法改进:钻石操作符使用升级
6.语法改进:try语句
7.String存储结构变更
8.集合工厂方法:快速创建只读集合
9.InputStream 加强
10.增强的 Stream API
takeWhile()的使用
dropWhile()的使用
ofNullable()的使用
iterate()重载的使用
11.Optional获取Stream的方法
Optional类中stream()的使用
12.Javascript引擎升级:Nashorn
17-2 Java 10 新特性
1.JDK10的12个JEP
2.局部变量类型推断
3.集合新增创建不可变集合的方法
17-3 Java 11 新特性
JDK 11 是一个长期支持版本(LTS, Long-Term-Support)
l 对于企业来说,选择 11 将意味着长期的、可靠的、可预测的技术路线图。其中免费的OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将是可以放心选择的版本。
l 从 JVM GC 的角度,JDK11 引入了两种新的 GC,其中包括也许是划时代意义的 ZGC,虽然其目前还是实验特性,但是从能力上来看,这是 JDK 的一个巨大突破,为特定生产环境的苛刻需求提供了一个可能的选择。例如对部分企业核心存储等产品,如果能够保证不超过 10ms 的 GC 暂停,可靠性会上一个大的台阶,这是过去我们进行 GC 调优几乎做不到的,是能与不能的问题。
官网公开的 17 个 JEP(JDK Enhancement Proposal 特性增强提议)
1.新增一系列字符串处理方法
2.Optional 加强
3.局部变量类型推断升级
4.全新的HTTP 客户端API
5.更简化的编译运行程序
6.废弃Nashorn引擎
废除Nashorn javascrip
t引擎,在后续版本准备移除掉,有需要的可以考虑使用GraalVM
。
7.ZGC
l 优势:
Ø GC暂停时间不会超过10ms
Ø 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
Ø 和G1相比, 应用吞吐能力不会下降超过15%
Ø 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
Ø 初始只支持64位系统
l ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。
8.其它新特性
l Unicode 10
l Deprecate the Pack200 Tools and API
l 新的Epsilon垃圾收集器
l 完全支持Linux容器(包括Docker)
l 支持G1上的并行完全垃圾收集
l 最新的HTTPS安全协议TLS 1.3
l Java Flight Recorder
17-4 在当前JDK中看不到什么
1.一个标准化和轻量级的JSON API
一个标准化和轻量级的JSON API被许多Java开发人员所青睐。但是由于资金问题无法在Java当前版本中见到,但并不会削减掉。Java平台首席架构师MarkReinhold
在JDK 9邮件列中说:“这个JEP将是平台上的一个有用的补充,但是在计划中,它并不像Oracle资助的其他功能那么重要,可能会重新考虑JDK 10或更高版本中实现。 ”
2.新的货币 API
3.展 望
十八、面试题
1.抽象类和接口的区别
声明方法的存在而不去实现它的类被叫做抽象类(abstract class)
,
1.它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例
。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。
2.不能有抽象构造函数或抽象静态方法。
3.Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
4.抽象类可继承实体类,但实体类必须不能是如下两种情况之一:
1,final
修饰符修饰的类是不能的。
2,如果此实体类有且仅有私有的构造函数也是不能的。
接口(interface)
是抽象类的变体。
1.在接口中,所有方法都是抽象的,没有一个有程序体。多继承性可通过实现这样的接口而获得。
2.接口只可以定义static final 成员变量
。
3.接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。
4.当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof
运算符可以用来决定某对象的类是否实现了接口。
5.接口可以继承接口。抽象类可以实现(implements
)接口。
2.Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)
匿名的内部类是没有名字的内部类。不能extends
(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现
3.谈谈final, finally, finalize的区别
final
—修饰符(关键字)
1. 如果一个类被声明为final
,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract
的,又被声明为final
的。
2.将变量或方法声明为final
,可以保证它们在使用中不被改变。
3.被声明为final
的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
4.被声明为final
的方法也同样只能使用,不能重载。
finally
1.在异常处理时提供 finally
块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch
子句就会执行,然后控制就会进入 finally
块(如果有的话)。
finalize
—方法名
1.Java 允许使用finalize()
方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
2.它是在 Object
类中定义的,因此所有的类都继承了它。
3.子类覆盖 finalize()
方法以整理系统资源或者执行其他清理工作。
4.finalize()
方法是在垃圾收集器删除对象之前对这个对象调用的。
4.int 和 Integer 有什么区别
1.Java 提供两种不同的类型:引用类型
和原始类型(或内置类型)
。
2.int
是java的原始数据类型,Integer
是java为int提供的封装类。Java为每个原始类型提供了封装类
。
3.原始类型封装类型及其对应的包装类:
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
doubl Double
4.引用类型和原始类型的行为完全不同,并且它们具有不同的语义。
5.引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题
,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null
,而原始类型实例变量的缺省值与它们的类型有关
。
5.什么是java序列化?如何实现java序列化?
1.序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。
2.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
3.序列化是为了解决在对对象流进行读写操作时所引发的问题。
4.序列化的实现:将需要被序列化的类实现Serializable
接口,该接口没有需要实现的方法,
5.implements Serializable
只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream
)来构造一个ObjectOutputStream
(对象流)对象,接着,使用ObjectOutputStream
对象的writeObject(Object obj)
方法就可以将参数为obj
的对象写出(即保存其状态),要恢复的话则用输入流。
6.数组有没有length()方法?String有没有length()方法?File有没有length()方法?ArrayList有没有length()方法?
数组没有length()
方法,但是有length
属性。 String
和File
有length()
方法。 ArrayList
没有length()
方法,有size()
方法获取有效元素个数。
7.JVM加载class文件的原理机制
JVM中类的装载是由ClassLoader(类加载器)
和它的子类来实现的。Java ClassLoader
是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
8.Collection 和 Collections的区别
Collection
是集合类的上级接口,继承于他的接口主要有Set
和List
Collections
是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
9.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别
Set
里的元素是不能重复的,用equals()
方法判读两个Set
是否相等
equals()
和==
方法决定引用值是否指向同一对象。equals()
在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值
10.List, Set, Map是否继承自Collection接口
List,Set
是,Map
不是
11.ArrayList,Vector,LinkedList的存储性能和特性
ArrayList
和Vector
都是使用数组
方式存储数据,此数组元素数大于实际存储的数据以便增加
和插入
元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢
。
Vector
由于使用了synchronized
方法(线程安全),通常性能上较ArrayList
差。
而LinkedList
使用双向链表
实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快,但是查找比较慢(相对于 ArrayList
和Vector
)。
12.HashMap和Hashtable的区别
1.HashMap
与Hashtable
都实现了Map
接口。由于HashMap
的非线程安全性,效率上可能高于Hashtable
。Hashtable
的方法是Synchronize
的,而HashMap
不是,在多个线程访问Hashtable
时,不需要自己为它的方法实现同步,而HashMap
就必须为之提供外同步。
2.HashMap
允许将null
作为一个entry
的key
或者value
,而Hashtable
不允许。
3.HashMap
把Hashtable
的contains
方法去掉了,改成containsvalue
和containsKey
。因为contains
方法容易让人引起误解。
4.Hashtable
继承自Dictionary
类,而HashMap
是Java1.2
引进的Map interface
的一个实现。
5.Hashtable
和HashMap
采用的hash/rehash算法
都大概一样,所以性能不会有很大的差异。
13.ArrayList和Vector的区别
就ArrayList
与Vector
主要从二方面来说.
一.同步性:Vector
是线程安全的,也就是说是同步的,而ArrayList
是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector
默认增长为原来一培,而ArrayList
却是原来的一半
14.集合类都有哪些?主要方法?
最常用的集合类是 List
和 Map
。 List
的具体实现包括 ArrayList
、LinkedList
和 Vector
,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List
适用于按数值索引访问元素的情形。
Map
提供了一个更通用的元素存储方法。 Map
集合类用于存储元素对(称作"键"和"值"),其中每个键映射到一个值。
15.String与StringBuffer的区别
String
的长度是不可变的,StringBuffer
的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer
,如果最后需要String
,那么使用StringBuffer
的toString()
方法
String
类是final
类故不可以继承
16.Java为什么要引入线程机制,线程、程序、进程之间的关系是怎样的。
线程
可以彼此独立的执行,它是一种实现并发机制的有效手段,可以同时使用多个线程来完成不同的任务,并且一般用户在使用多线程时并不考虑底层处理的细节。
程序
是一段静态的代码,是软件执行的蓝本。进程是程序的一次动态执行过程,即是处于运行过程中的程序。
线程
是比进程
更小的程序执行单位,一个进程可以启动多个线程同时运行,不同线程之间可以共享相同的内存区域和数据。多线程程序是运行时间后嗣可能出现在一个进程之内的、有一个以上线程同时运行的情况的程序。
17.Runnable接口包括哪些抽象方法?Thread类有哪些主要域和方法?
Runnable
接口中仅有run()
抽象方法。
Thread
类主要域有:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY
。
主要方法有start(),run(),sleep(),currentThread(),setPriority(),getPriority(),join()
等。
18.创建线程有哪两种方式(jdk5.0之前)?试写出每种的具体的流程。比较两种创建方式的不同,哪个更优。
1—继承Thread
类
-
定义类继承
Thread
类。 -
覆盖
Thread
类中的run
方法。 -
创建
Thread
子类对象,即创建了线程对象。 -
调用线程对象
start
方法:启动线程,调用run
方法。
2—实现Runnable
接口
1)定义类,实现Runnable
接口。
2)覆盖Runnable
接口中的run
方法。
3)通过Thread
类建立线程对象。
4)将Runnable
接口的子类对象作为实际参数传递给Thread
类的构造方法中。
5)调用Thread
类的start
方法:开启线程,调用Runnable
子类接口的run
方法。
【区别】
继承Thread
: 线程代码存放Thread
子类run
方法中。
实现Runnable
:线程代码存在Runnable
子类接口的run
方法。
【实现Runnable
接口的好处】
1)避免了单继承的局限性
2)多个线程可以共享同一个接口子类的对象,非常适合多个相同线程来处理同一份资源。
19.运行时异常与一般异常有何异同
异常
表示程序运行过程中可能出现的非正常状态,运行时异常
表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明
抛出可能发生的非运行时异常,但是并不要求必须声明
抛出未被捕获的运行时异常。
20.关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
1.Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象
,它是Throwable
类或其它子类的实例。
2.当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。
3.Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally
。
4.一般情况下是用try
来执行一段程序,如果出现异常,系统会抛出(throws
)一个异常,这时候你可以通过它的类型来捕捉(catch
)它,或最后(finally
)由缺省处理器来处理。用try
来指定一块预防所有"异常"的程序。紧跟在try
程序后面,应包含一个catch
子句来指定你想要捕捉的"异常"的类型。
5.throw
语句用来明确地抛出一个"异常"。
6.throws
用来标明一个成员函数可能抛出的各种"异常"。
7.Finally
为确保一段代码不管发生什么"异常"都被执行一段代码。
8.可以在一个成员函数调用的外面写一个try
语句,在这个成员函数内部写另一个try
语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈
上面,直到所有的try
语句都完成。如果下一级的try
语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try
语句。
21.try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
会执行,在return
前执行
22.java中有几种方法可以实现一个线程(jdk5.0之前)?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
有两种实现方法,分别是继承Thread
类与实现Runnable
接口。
用synchronized
关键字修饰同步方法,反对使用stop()
,是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
suspend()
方法容易发生死锁。调用suspend()
的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend()
,而应在自己的Thread类中
置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()
命其进入等待状态。若标志指出线程应当恢复,则用一个notify()
重新启动线程。
23.sleep() 和 wait() 有什么区别?
sleep
是线程类(Thread
)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep
不会释放对象锁。
wait
是Object
类的方法,对此对象调用wait
方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify
方法(或notifyAll
)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
24.启动一个线程是用run()还是start()?
启动一个线程是调用start()
方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM
调度并执行。这并不意味着线程就会立即运行。
run()
方法就是正常的对象调用方法的执行,并不是使用分线程来执行的。
25.请说出你所知道的线程同步的方法。
wait()
:使一个线程处于等待状态,并且释放所持有的对象的lock
。
sleep()
:使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException
异常。
notify()
:唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM
确定唤醒哪个线程,而且不是按优先级。
notityAll()
:唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
26.线程的基本概念、线程的基本状态以及状态之间的关系
线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。
Java中的线程有五种状态,分别是:创建、就绪、运行、阻塞、结束
。
27.简述synchronized和java.util.concurrent.locks.Lock的异同 ?
主要相同点:Lock
能完成synchronized
所实现的所有功能
主要不同点:Lock
有比synchronized
更精确的线程语义和更好的性能。synchronized
会自动释放锁,而Lock
一定要求程序员手工释放,并且必须在finally
从句中释放。
28.多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
JDK5.0之前,多线程有两种实现方法,分别是继承Thread
类与实现Runnable
接口
JDK5.0之后新增两种,一是实现Callable
接口,二是使用线程池
同步的实现方面有两种,一是synchronized
,二是wait与notify
29.接口
1.在使用 interface 声明一个外部接口时,只可以使用( D)修饰符修饰该接口。
-
A. private
-
B. protected
-
C. private protected
-
D. public
解析:
jdk7:
接口的方法都是 public abstract 变量都是public static final的
jdk8:
接口可以定义 static方法 与 default方法。 static方法只能通过接口调用,不能通过实现类调用。default只能通过接口实现类调用,不能通过接口名调用。
2.下面哪些类实现或者继承了Collection接口?( B C )
-
HashMap
-
ArrayList
-
Vector
-
Iterator
解析:
30.类
1.设A为已知定义的类名,下列声明A类的对象a的语句( D)
-
A. float A a
-
B. public a=A()
-
C. A a=new int ()
-
D. A a=new A()
解析:
A a;声明
A a =new A( );实例化
题目问的不是很准确
2.以下哪一个类没有实现java.util.Map接口?(C)
-
A. Hashtable
-
B. HashMap
-
C. Vector
-
D. IdentityHashMap
解析:
//直接看源码的定义
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public class IdentityHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, java.io.Serializable, Cloneable
A,B,D都实现了Map接口,其中A与B的区别是Hashtable是线程安全的,HashMap是线程不安全的
C中Vector是实现了List接口,是一个线程安全的List
3.类方法中可以直接调用对象变量。( B )
-
A. 正确
-
B. 错误
解析:
静态方法中不能调用对象的变量,因为静态方法在类加载时就初始化,对象变量需要在新建对象后才能使用
31.修饰符
abstract
1.下列关于修饰符混用的说法,错误的是( D)
-
abstract不能与final并列修饰同一个类
-
abstract 类中不建议有private的成员
-
abstract 方法必须在abstract类或接口中
-
static方法中能直接处理非static的属性
解析:
A、abstract修饰的类,不可实例化,所以需要子类去继承,然后重写其中的抽象方法。但是final修饰类是不可继承的。两者属性相冲。
B、看清楚,人家说的是不建议有,不是不能有。
C、抽象类中可以没有抽象方法,但是抽象方法必须在抽象类中或者接口中
D、static不可以修饰非static的属性,因为类加载的时候,static属性比非static先初始化,那么一个存在的总不能访问一个没有存在的吧。
abstract 抽象 final 常量
A、被abstract 修饰的类表示必须被其他类继承使用,而被final修饰的类,表示不能作为其他类的父类,两者矛盾。故不能同时使用
B、abstract 类里可以有private成员,但是 abstract 类是要被继承使用,也就是说该类的属性方法是要被其他类所访问使用的,如果有属性或者方法被private修饰,表示只能内部使用,两者似乎又矛盾了。故不建议有private成员
C、抽象方法只能声明在抽象类或者接口中,这个是母庸质疑的
D、类方法(static修饰的)不能直接使用实例方法,必须实例化后,利用对象调用方法,才能处理。故D错误
不管是静态方法还是静态成员,都是类级别存在的,也就是说随着类的加载而加载,优先于对象的存在,而非静态成员和方法是对象级别的存在,所以在静态方法中调用非静态的成员或方法(此时还不存在对象),是不可能的,但是反过来是可以的:非静态中调用静态。于是也就有静态方法中不能使用this和super关键字。
2.在jdk1.8之前,下列哪一种叙述是正确的( D )
-
abstract修饰符可修饰字段、方法和类
-
抽象方法的body部分必须用一对大括号{ }包住
-
声明抽象方法,大括号可有可无
-
声明抽象方法不可写出大括号
解析:
A:abstract修饰符用来修饰类和成员方法,
1:用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化。
2:用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。
B、C:抽象方法没有方法体,有没有方法体看有没有大括号。抽象方法不能有函数执行体,如果用{}包住,即是没有返回值的空白函数体,不符合抽象方法的语法。
32.线程
以下哪个不能用来处理线程安全(D)
-
synchronized关键字
-
volatile关键字
-
Lock类
-
transient关键字
解析:
synchrozied关键字:称作同步,主要用来给方法、代码块加锁,被加锁的代码段,同一时间内多线程同时访问同一对象的加锁方法/代码块时,只能有一个线程执行能执行方法/代码块中的代码,其余线程必须等待当前线程执行完以后才执行该方法/代码块。
volatile关键字:1.保证了不同线程对该变量操作的内存可见性.(当一个线程修改了变量,其他使用次变量的线程可以立即知道这一修改)。2.禁止了指令重排序. volatile不是保护线程安全的。它保护的是变量安全。主要的功能是保护变量不被主函数和中断函数反复修改造成读写错误。与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。
Lock接口:提供了与synchronized关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
transient关键字:简单地说,就是让某些被修饰的成员属性变量不被序列化。
扩展:
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
目的:1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
2.以下哪个事件会导致线程销毁?( D )
-
调用方法sleep()
-
调用方法wait()
-
start()方法的执行结束
-
run()方法的执行结束
解析:
A. 调用sleep()方法让线程进入睡眠状态—睡眠指定的时间后再次执行;
B. 调用wait()方法让线程进入等待状态 ----等待别的线程执行notify()或notifyAll()唤醒后继续执行;
C.调用start()方法让线程进入就绪状态—得到CPU时间就执行线程;
D.run()方法是线程的具体逻辑方法,执行完,线程就结束。
33.构造代码块
检查程序,是否存在问题,如果存在指出问题所在,如果不存在,说明输出结果。
class HelloA
{
public HelloA() { }
{
System.out.println("I’m A class");
}
static
{
System.out.println("static A");
}
}
public class HelloB extends HelloA
{
public HelloB(){}
{
System.out.println("I’m B class");
}
static
{
System.out.println("static B");
}
public static void main(String[] args)
{
new HelloB();
}
}
输出为:
static A
static B
I’m A class
I’m B class
解析:
Java程序初始化工作可以在许多不同的代码块中来完成,它们的执行顺序如下:
父类的静态变量、父类的静态代码块、子类的静态变量、子类的静态代码块、
父类的非静态变量、父类的非静态代码块、父类的构造函数、
子类的非静态变量、子类的非静态代码块、子类的构造函数。
1.静态代码块 2.构造代码块3.构造方法的执行顺序是1>2>3;明白他们是干嘛的就理解了。
①.静态代码块:是在类的加载过程的第三步初始化的时候进行的,主要目的是给类变量赋予初始值。
②.构造代码块:是独立的,必须依附载体才能运行,Java会把构造代码块放到每种构造方法的前面,用于实例化一些共有的实例变量,减少代码量。
③.构造方法:用于实例化变量。
1是类级别的,2、3是实例级别的,自然1要优先23.
明白一点:对子类得主动使用会导致对其父类得主动使用,所以尽管实例化的是子类,但也会导致父类的初始化和实例化,且优于子类执行。
扩展:
Java中静态代码块、构造代码块、构造函数、普通代码块:
1.静态代码块
2.构造代码块
3.构造函数与普通代码块
4.执行顺序
静态代码块>构造代码块>构造函数>普通代码块
34.javac-d
命令javac-d参数的用途是?( A )
-
指定编译后类层次的根目录
-
指定编译时需要依赖类的路径
-
指定编译时的编码
-
没有这一个参数
解析:
35.JVM
以下哪个不属于JVM堆内存中的区域( B)?
-
survivor区
-
常量池
-
eden区
-
old区
解析:
jvm堆分为:新生代(一般是一个Eden区,两个Survivor区),老年代(old区)。
常量池属于 PermGen(方法区)
36.基本数据类型运算
以下代码执行的结果显示是多少( D )?
-
true,false,true
-
false,true,false
-
true,true,false
-
false,false,true
解析:
其实当我们在为Integer赋值的时候,java编译器会将其翻译成调用valueOf()方法。
比如Integer i=127翻译为Integer i=Integer.valueOf(127)
然后我们来看看valueOf()函数的源码:
public static Integer valueOf(int i)
{
//high为127
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.***[i + 128];
else
return new Integer(i);
}
可以看出,对于-128到127之间的数,Java会对其进行缓存。而超出这个范围则新建一个对象。
所以现在回到这道问题
i1和i2为128,超出范围,所以都需要新建对象,对象比较为false;
关于字符串的解释:String str1=”java”; //指向字符串池
String str2=”blog”; //指向字符串池
/*
s是指向堆中值为"javablog"的对象,+运算符会在堆中建立来两个String对象,
这两个对象的值分别是"java" "blog". 也就是说从字符串池中复制这两个值,
然后在堆中创建两个对象,然后再建立对象s,然后将"javablog"的堆地址赋给s.
*/
String s=str1+str2;
System.out.println(s==”javablog”); //结果是false。
Jvm确实对型如String str1=”java”;的String对象放在常量池里,但是它是在编译时那么做的,
而String s=str1+str2;是在运行时刻才能知道,也就是说str1+str2是在堆里创建的,所以结果为false了
i5和i6为100,在范围之内,在执行Integer i5=100时,就会直接缓存到内存中,
但执行执行Integer i6=100时,就直接从缓存里取,而不需要新建对象,所以为true。
2.有如下一段代码,请选择其运行结果( C )
public class StringDemo{
private static final String MESSAGE="taobao";
public static void main(String [] args) {
String a ="tao"+"bao";
String b="tao";
String c="bao";
System.out.println(a==MESSAGE);
System.out.println((b+c)==MESSAGE);
}
}
-
true true
-
false false
-
true false
-
false true
解析:
37.servlet
1.下列有关Servlet的生命周期,说法不正确的是?( A )
-
在创建自己的Servlet时候,应该在初始化方法init()方法中创建Servlet实例
-
在Servlet生命周期的服务阶段,执行service()方法,根据用户请求的方法,执行相应的doGet()或是doPost()方法
-
在销毁阶段,执行destroy()方法后会释放Servlet 占用的资源
-
destroy()方法仅执行一次,即在服务器停止且卸载Servlet时执行该方法
解析:
Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。
(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet
(2)创建:通过调用servlet构造函数创建一个servlet对象
(3)初始化:调用init方法初始化
(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
(5)卸载:调用destroy方法让servlet自己释放其占用的资源
38.final
1.下列关于final、finally、finalize说法正确的是( A B D)
-
final可以用来修饰类、方法、变量
-
finally是java保证重点代码一定要被执行的一种机制
-
变量被final修饰后不能再指向其他对象,但可以重写
-
finalize设计的目的是保证对象在被垃圾收集前完成特定资源的回收
解析:
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
2.以下关于final关键字说法错误的是( A C )
-
final是java中的修饰符,可以修饰类、接口、抽象类、方法和属性
-
final修饰的类肯定不能被继承
-
final修饰的方法不能被重载
-
final修饰的变量不允许被再次赋值
解析:
1.final修饰变量,则等同于常量
2.final修饰方法中的参数,称为最终参数。
3.final修饰类,则类不能被继承
4.final修饰方法,则方法不能被重写。
5.final 不能修饰抽象类
6.final修饰的方法可以被重载 但不能被重写
喜欢请关注我
至此,我们的Java的学习(下)
就讲解完成了。源码素材可以通过关注我的微信公众号 我爱学习呀嘻嘻
,回复关键字Java的学习源码素材
进行获取哦。