Java泛型
Java泛型
引用自java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
概述泛型,即”参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。 也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法种,分别被称为泛型类、泛型接口、泛型方法。
示例1234567List list = new ArrayList();list.add("aaa");list.add(100);for (int i = 0; i < list.size(); i++) { String item = (String) list.get(i); System.out.println(ite ...
LinkedHashMap
LinkedHashMap大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。
LinkedHashMap解决了这个问题,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。
关注点
集合关注点
结论
LinkedHashMap是否允许空
Key和Value都允许为空
LinkedHashMap是否允许重复数据
Key重复会覆盖、Value允许重复
LinkedHashMap是否有序
有序
LinkedHashMap是否线程安全
非线程安全
基本数据结构关于LinkedHashMap,先提两点:
LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序
LinkedHashMap的基本实 ...
LinkedList
LinkedList
引用自图解集合 2 :LinkedList
定义LinkedList是基于链表实现的,所以先讲解一下什么是链表。链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储单元里面除了存放有待存储的数据以外,还存储有其下一个存储单元的地址(下一个存储单元的地址是必要的,有些存储结构还存放有其前一个存储单元的地址),每次查找数据的时候,通过某个存储单元中的下一个存储单元的地址寻找其后面的那个存储单元。
这么讲可能有点抽象,先提一句,LinkedList是一种双向链表,双向链表我认为有两点含义:
链表中任意一个存储单元都可以通过向前或者向后寻址的方式获取到其前一个存储单元和其后一个存储单元
链表的尾节点的后一个节点是链表的头结点,链表的头结点的前一个节点是链表的尾节点
包含元素LinkedList既然是一种双向链表,必然有一个存储单元,看一下LinkedList的基本存储单元,它是LinkedList中的一个内部类:
1234567891011private static class Node‹E› ...
List相关
List相关类图
List接口继承Collection接口,List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。
List实现类ArrayListArrayList时基于数组实现的List类,它封装了一个可以动态扩容的数组。详情见ArrayList源码分析。
LinkedListLinkedList实现了List和Deque接口,具备列表和双端队列的一些特性。详情见LinkedList源码分析。
VectorVector和ArrayList在用法上几乎完全相同,并且Vector时线程安全的。Stack是Vector的一个子类,实现了”栈”这种数据接口,栈的特性是LIFO后进先出。
Map相关
Map相关类图
Map用于保存具有”映射关系”的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回false。
Map的实现类和子接口中key集的存储形式和Set集合相同(即key不能重复);Map的实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据索引进行查找)。
Map实现类HashMapHashMap保存的key-value对是无序的,判断HashMap中的两个key是否相等的标准是:两个key通过equals()方法比较返回true、同时两个key的hashCode值也必需相等。
LinkedHashMapLinkedHashMap使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致。
HashTable线程安全的Map实现类。
PropertiesProperties对象在处理属性文 ...
Queue相关
Queue相关类图
Queue用于模拟”队列”这种数据结构,其特性为FIFO先进先出。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问元素。
Queue实现类PriorityQueuePriorityQueue优先级队列,它并不是一个比较标准的队列实现,PriorityQueue保存元素的顺序并不是按照加入队列的顺序,而是按照队列元素的大小进行排序的。
DequeDeque双端队列接口,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用。
ArrayDequeArrayDeque是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个可动态扩容的Object[]数组来存储集合元素。
Runnable
Runnable在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。
Runnable的中文意思是”任务”,顾名思义,通过实现Runnable接口,我们 定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread类的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这跟普通的方法调用没有任何区别。
事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。
在Java中,继承Thread类和实现Runnable接口这两种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。
Set相关
Set相关类图Set接口继承Collection接口
Set集合里的多个对象之间没有明显的顺序。Set继承自Collection接口,不能包含有重复元素(这是整个Set相关类的共有属性)。
Set判断两个对象相同使用的是equals方法。也就是说,当Set中加入一个新元素时,如果这个新元素对象和Set中已有对象进行equals比较都返回false,则Set就会接收这个新元素对象加入,否则拒绝。
因为Set的这个制约,在使用Set集合的时候,需要为Set集合里的元素的实现类实现一个有效的equals(Object)方法。
Set实现类HashSet
集合关注点
结论
HashSet是否允许空
允许
HashSet是否允许重复数据
不允许
HashSet是否有序
无序
HashSet是否线程安全
非线程安全
HashSet时Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。HashSet的底层实现其实是一个HashMap,该HashMap存储的键是元素对象,值则是HashSet中定义的PRESENT变量(Obj ...
TCP编程
TCP编程
引用自JAVA 通过 Socket 实现 TCP 编程
TCP简介TCP(Transmission Control Protocol传输控制协议)是一种面向 连接的、可靠的、基于字节流 的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层 传输层 所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上、应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)的限制)。之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如 ...
Thread
Thread线程的实现方式之一,继承Thread类。
在java.lang包中定义,继承Thread类必须重写run()方法。
创建好了自定义的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。注意,不是调用run()方法启动线程,run()方法中只是定义需要执行的任务,如果调用run()方法,即相当于在主线程中执行run()方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。新线程创建的过程不会阻塞主线程的后续执行。
UDP编程
UDP编程
引用自JAVA Socket 实现 UDP 编程
UDP简介UDP是User Datagram Protocal的简称,中文名是用户数据报协议,是OSI(Open System Interconnection,开方式系统互联)参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17。
UDP有 不提供数据包分组、组装 和 不能对数据博爱进行排序 的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多C/S模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使在今天UDP仍然不失为一种非常实用和可行的网络传输层协议。
与所熟知的TCP(传输控制协议)一样,UDP直接位于IP(网际协议)的顶层。根据OSI参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数 ...
Callable
Callable在Java中,使用ExecutorService、Callable、Future可以实现有返回结果的多线程。
ExecutorService、Callable、Future都是属于Executor框架中的功能类。详细了解Executor框架可以参考java并发编程-Executor框架。返回结果的线程是在JDK1.5中引入的新特征。
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须实现Runnable接口。执行Callable任务后,可以获取一个Future对象,在该对象上调用get方法就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现有返回结果的多线程了。
123456789101112131415161718192021222324252627282930313233343536373839public class MultiThreadSample { public static void main(String[] args) throws ExecutionExc ...
package与jar包
package与jar包
引用自JAVA 包与包之间访问(package)
package的用处Java中的package用处和特点有:
对类文件进行分类管理。
给类提供多层命名空间。
写在程序文件的第一行。
类名的全称是:包名.类名
包也是一种封装形式。
package之间的访问包之间访问时,需要遵循以下规则:
包与包之间进行访问,被访问的包中的类以及类中的成员,需要用public修饰。
不同包中的子类可以直接访问父类中被protected修饰的成员。
包与包之间可以使用的权限只有两种,public和protected。
访问权限具体的对应关系如下表:
访问类-被访问类的修饰符-能否访问
public
protected
default
private
同一类中
√
√
√
√
同一包中
√
√
√
×
子类
√
√
×
×
不同包类中
√
×
×
×
import导入package通过import可以简化类名的书写。包名一般使用url,因为url具有唯一性,例如:package com.qq.demo;
导包的写法为:impor ...
动态代理
动态代理
引用自JAVA动态代理
具体场景为了使代理类和被代理类对第三方有相同的函数,代理类和被代理类一般实现一个公共的interface,该interface定义如下
1234public interface Calculator { public Integer add(Integer num1, Integer num2); public Integer minus(Integer num1, Integer num2);}
被代理类定义如下
1234567891011121314151617public class CalculatorImpl implements Calculator { @Override public Integer add(Integer num1, Integer num2) { int ret = num1 + num2; System.out.println("in calculatorImpl, res: " + ret); ...
压缩流
压缩流1234567891011121314151617181920private static void testZipFile() { File file = new File(FILENAME); File zipFile = new File(ZIP_FILENAME); try { InputStream is = new FileInputStream(file); ZipOutputStream zipOs = new ZipOutputStream(new FileOutputStream(zipFile)); zipOs.putNextEntry(new ZipEntry(file.getName())); zipOs.setComment("Test ZIP"); int temp; while ((temp = is.read()) != -1) { zipOs.write(temp); ...
基本数据类型
基本数据类型
引用自Java 基本数据类型
变量就是申请内存来储存值。当创建变量得时候,需要在内存中申请空间。内存管理系统根据变量的类型为其分配存储空间,分配的空间只能用来储存该类型数据。
Java中有两大数据类型:内置数据类型、引用数据类型。
内置数据类型Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
类型
长度
最小值
最大值
默认值
示例
备注
byte
8位
-128(-2^7)
127(2^7 - 1)
0
byte a = 100, byte b = -50
byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一
short
16位
-2^15
2^15 - 1
0
short s = 1000, short r = -20000
Short 数据类型也可以像 byte 那样节省空间,一个short变量是int型变量所占空间的二分之一
int
32位
-2^31
2^31 - 1
0
int a ...
字符流
字符流
初始化变量
1private static final String FILENAME = "E:" + File.separator + "hello.txt";
向文件中写入数据123456789101112private static void testWriteFile() { File file = new File(FILENAME); try { //如果是向文件追加内容,则改为 new FileWriter(file, true) Writer writer = new FileWriter(file); String txt = "Hello, World!"; writer.write(txt); writer.close(); } catch (IOException e) { e.printStackTrace(); }}
...
字节流
字节流
初始化变量
1private static final String FILENAME = "E:" + File.separator + "hello.txt";
向文件中写入字符串1234567891011121314151617181920212223242526272829private static void testWriteFile() { File file = new File(FILENAME); try { //默认覆盖原内容,不向文件中追加新内容 OutputStream os = new FileOutputStream(file); String txt = "你好,世界!"; byte[] bytes = txt.getBytes(); os.write(bytes); os.close(); } catch (IOException e) { ...
序列化与反序列化
序列化与反序列化
引用自深入分析Java的序列化与反序列化、Java对象的序列化与反序列化
序列化与反序列化序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个存储媒介,例如档案或是记忆体缓冲等。在网络传输过程中,可以是字节或是XML等格式。而通过字节或XML编码格式可以还原完全相等的对象,这个还原的过程称为反序列化。
Java对象的序列化与反序列化在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中。只有JVM处于运行状态的收,这些对象才可能存在。一旦JVM停止运行,这些对象的状态也就随之丢失了。
在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java的对象序列化可以帮助我们实现该功能。
对象序列化机制(object serialization)是Java语言内建的一种对象持久化方式,通过对象序列化,可以把对象的状态保存为字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的 ...
异常
异常异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
异常发生的原因由很多,通常包含以下几类:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。要理解Java异常处理是如何工作的,需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户的错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在的文件是,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译时是检查不到的。
图中红色部分为受检查异常。它们必须被捕获,或者在方法中声明为抛出该异常。