目录
一、Java集合
二、IO流
三、泛型
四、网络编程
一、Java集合
1、Java集合概述
- 集合就是对数据进行储存的容器,这里的储存是指在内存中的储存,持久化的储存在之后的SQL中会学到。
- 解决数组存储数据方面的弊端。
java集合与数组的比较
- 数组长度定义后不可变,集合可变
- 数组只能存储相同类型元素,集合可以存储不同的数据类型
- 数组中提供的方法优先,集合中有各种各样的方法对数据进行操作
2、集合分类
常用的集合:
- Collection接口:用来存储一个一个的对象
- List接口:存储有序的可重复的数据
- ArrayList
- LinkedList
- Vector
- Set接口:存储无序的,不可重复的数据
- HashSet
- LinedHashSet
- TreeSet
- List接口:存储有序的可重复的数据
- Map接口:用来储存key—value对的数据
- HashMap
- LinedHashMap
- TreeMap
- HashTable
- HashMap
3、Collection接口
- 用来存储一个一个的数据
- 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
- 有list和set实现接口
- Collection接口常用方法:
- add(Object obj) 向集合添加元素,若操作成功返回true
- addAll(Collection coll) 向集合添加所有元素,成功返回true
- size() 集合元素个数
- isEmpty() 是否为空
- clear() 清空集合
- contains(Object obj) 判断集合是否包含obj
- containsAll(Collection coll) 判断集合是否包含coll中所有元素
- remove(Object obj) 从集合中删除一个指定元素
- removeAll(Collection coll) 从集合中删除所有在集合 coll 中出现的元素
- retainsAll(Collection coll) 从集合中删除集合 coll 里不包含的元素
集合与数组的转换:
- 集合转数组:collection1.toArray()
- 数组转集合:Arrays.asList()
List接口:存储有序的,可重复的数据
- ArrayList:list接口主要实现类,线程不安全,效率高,底层用Object数组存储
JDK1.7new的时候底层创建长度为10的Object数组,默认情况下扩容为原来的1.5倍
JDK1.8new时的底层中数组初始化为{},第一次调用add()时,底层才创建长度10的数组,扩容为原来的1.5倍
- LinkedList:底层使用双向链表储存,频繁删除插入操作效率高
新建对象时,内部声明了Node类型的first和last属性,默认值为null
- Vector:List接口古老实现类,线程安全,效率低,底层也用Object数组储存
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。默认扩容为原来的2倍。
常用方法:
- 增:add(Object obj)
- 删:
- remove(int index)
- remove(Object obj)
- 改:set(int index, Object obj)
- 查:get(int index)
- 插:add(int index, Object obj)
- 长度:size()
- 遍历:
- Iterator
- 增强for循环
- 普通的循环
Set接口:存储无序的,不可重复的数据
- HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
- LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
- TreeSet:可以照添加对象的指定属性,进行排序。(自然排序、定制排序)
元素添加的过程(HashSet举例)
HashSet底层:JDK1.7 数组+链表 (底层用的HashMap创建的...)
- 添加元素,先调用元素hashCode方法,得到hash值
- 根据hash值通过某种算法来计算出在底层中的存放位置,
- 如果HashSet位置上无元素,直接添加
- 如果位置上已经有元素的情况下,比较元素之间的hash值
- 若hash值都不同,就添加成功
- 若hash值相同,就调用元素所在类的equals()方法
- equals方法返回false,添加成功
- equals方法返回true,添加失败
注意:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
4、Map接口:存储key—value的数据
- Map
- HashMap:作为Map的主要实现类,线程不安全的,效率高;存储null的key和value
- LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历,
- TreeMap:以添加的key-value对进行排序。此时考虑key的自然排序或定制排序,要求key必须是由同一个类创建的对象
- HashTable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
- Properties:配置文件常用 (key=value)
- HashMap:作为Map的主要实现类,线程不安全的,效率高;存储null的key和value
Map(key,value): 构成Entry对象
- key: 无序的不可重复的,用set储存 ,key所在类重写equals()和hashcode()方法
- value: 无序的可重复的,用Collection储存,value所在类重写equal()方法
HashMap底层原理:(jdk1.7)
- 在实例化后创建长度为16的Entry[]数组
- 添加元素流程:
- 计算所在类hashcode()方法,并找位置
- 若位置数据为空,添加成功
- 若位置不为空,与该位置上所有元素比较hash值
- 若哈希值都不同,添加成功
- 若哈希值有一个相同,则比较该类equals方法
- equals返回true,添加失败
- equals返回false,添加成功
- 扩容:默认扩容为原来容量的两倍
- 底层结构:数组+链表。
HashMap在JDK1.8的变化:
- new HashMap():底层没创建一个长度为16的数组
- jdk 8底层的数组是:Node[],而非Entry[]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk8中底层结构:数组+链表+红黑树。
- 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
- 当数组某一个链表长度大于8,且数组长度大于64时,将该位置下的链表改为红黑树存储
- DEFAULT_INITIAL_CAPACITY : HashMap默认容量:16
- DEFAULT_LOAD_FACTOR:HashMap默认加载因子:0.75
- threshold:扩容临界值 = 容量 * 加载因子
- TREEIFY_THRESHOLD: 数组某一元素中的链表长度大于该默认值,转化为红黑树 : 8
- MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
常用方法:
- 添加:put(Object key,Object value)
- 删除:remove(Object key)
- 修改:put(Object key,Object value)
- 查询:get(Object key)
- 长度:size()
- 遍历:keySet() / values() / entrySet()
5、Collections工具类
用来操作Collection和Map的工具类
- reverse(List):反转 List 中元素的顺序
- shuffle(List):对 List 集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定 List 集合元素升序排序
- sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
- swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
- Object min(Collection)
- Object min(Collection,Comparator)
- int frequency(Collection,Object):返回指定集合中指定元素的出现次数
- void copy(List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所旧值
注:ArrayList和HashMap都是线程不安全的,如果程序要求线程安全,我们可以将ArrayList、HashMap转换为线程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)
二、IO流
1、IO流概念
我们需要读取计算机上的文件信息,对于计算机来说,我们读取到的数据都是“100100101110”的字节数据,如何将数据读取出来呢,就需要一个管道,将文件中的数据以类似字节流的方式进行读取出来
流的分类:
- 按操作数据单位:字节流、字符流
- 数据的流向:输入流、输出流
- 流的角色:节点流、处理流
流的体系结构:
- 抽象基类
- 访问文件
- 访问数组
- 缓冲流
- 转化流
- 对象流
- 打印流等
学习IO流,首先我们需要了解file类
2、File类
- 一个文件或者一个目录都可以称为一个File类的对象
- File类在java.io包下
- File类中涉及到有关文件的创建、删除、重命名、修改时间、文件大小等方法,写入、读取操作由IO流来实现
- File有多个重载的构造器
File方法:
3、节点流
①、FileReader/FileWriter的使用 字节流
public void testFileReaderFileWriter() {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//不能使用字符流来处理图片等字节数据
//2.创建输入流和输出流的对象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while((len = fr.read(cbuf)) != -1){
//每次写出len个字符
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
//方式二:
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
②FileInputStream / FileOutputStream的使用 字符流
public void testFileInputOutputStream() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1.建文件
File srcFile = new File("1.jpg");
File destFile = new File("2.jpg");
//2.建流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3.复制的过程
byte[] buffer = new byte[5];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关流
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4、缓冲流
提供流的读取、写入的速度,内存中提供一个缓冲区,默认8kb
- BufferedInputStream
- BufferedOutputStream
//实现文件复制的方法
public void copyFileWithBuffered(String srcPath,String destPath){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.造流
//2.1 造节点流
FileInputStream fis = new FileInputStream((srcFile));
FileOutputStream fos = new FileOutputStream(destFile);
//2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节:读取、写入
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的关闭
//先关闭外层的流
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭外层流的同时,内层流也会自动的进行关闭.我们可以省略
// fos.close();
// fis.close();
}
}
- BufferedReader
- BufferedWriter
public void testBufferedReaderBufferedWriter(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建文件和相应的流
br = new BufferedReader(new FileReader(new File("dbcp.txt")));
bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));
//读写操作
//方式二:使用String
String data;
while((data = br.readLine()) != null){
//方法二:
bw.write(data);//data中不包含换行符
bw.newLine();//提供换行的操作
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5、转换流
提供字节流与字符流之间的转换
- InputStreamReader:将一个字节的输入流转换为字符的输入流
- OutputStreamWriter:将一个字符的输出流转换为字节的输出流
public void test2() throws Exception {
//1.造文件、造流
File file1 = new File("dbcp.txt");
File file2 = new File("dbcp_gbk.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
//2.读写过程
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
//3.关流
isr.close();
osw.close();
}
6、其他流
①、标准的输入输出流:
- System.in:标准的输入流,默认从键盘输入
- System.out:标准的输出流,默认从控制台输出
②、打印流:用于多种数据类型的输出
- PrintStream
- PrintWriter
③、数据流:用于读取或写出基本数据类型的变量或字符串
- DataInputStream
- DataOutputStream
④、对象流:
- ObjectOutputStream:内存中的对象--->存储中的文件、通过网络传输出去:序列化过程
- ObjectInputStream:存储中的文件、通过网络接收过来 --->内存中的对象:反序列化过程
实现序列化的对象所属的类需要满足:
- 需要实现接口:Serializable
- 当前类提供一个全局常量:serialVersionUID
- 除了当前Person类需要实现Serializable接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
7、其他数据传输API
①随机存取文件流:RandomAccessFile
- RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
- RandomAccessFile既可以作为一个输入流,又可以作为一个输出流
- 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。
- 如果写出到的文件存在,则会对原文件内容进行覆盖。(默认情况下,从头覆盖)
②Path类(jdk7+)
- Path替换原有的File类。
- 常用方法
三、泛型(JDK1.5+)
1、泛型的概念
泛型允许在定义类、接口的时候,通过一个标志来设定模糊的参数类型,可以是返回值类型,也可以是传入参数类型。当对象创建时才能确定到底创建的什么类型的数据。
2、泛型的使用
示例:①List<Integer> list = new List<Integer>();
②Set<Map.Entry<String,Integer>> entry = map.entrySet();
- 在<>里来设定传入的参数类型,定义好后就只能传入该数据类型的变量,否则就会报错。
- 在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
- 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
- 如果实例化时没有声明泛型类型,默认为java.long.Object类型
3、自定义泛型
泛型类:在类定义后定义泛型 类的内部结构就可以使用类的泛型
public class Order<T> {
String Name;
int Id;
//类的内部结构就可以使用类的泛型
T orderT;
}
泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没任何关系。
public static <E> List<E> myMethod(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
//如下的不是泛型方法
public T getOrderT(){
return orderT;
}
4、泛型的特性
- 泛型可能有多个参数,此时应该将多个参数一起放入<>中,<E1,E2,E3>
- 泛型的构造器没有<E>,
- 泛型不同的引用不能相互赋值
- ArrayList<String>和ArrayList<Integer>在编译时时两种类型,但是在运行时刻,只有一个ArrayList被加载到JVM中
- 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
- 异常类没有泛型
- 父类有泛型,子类可以保留父类泛型,也可以选择指定泛型类型。
-
虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。
-
补充:类A是类B的父类,A<G> 是 B<G> 的父类
5、泛型中的通配符
通配符:?
- 类A是类B的父类,G<A>和G<B>是没关系的,二者共同的父类是:G<?>
限制条件的通配符的使用
- ? extends A:
- G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
- ? super A:
- G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
- G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
四、网络编程
1、网络编程的概念
①实现网络通信需要解决的两个问题
- 如何准确定位一台或多台主机 -----> IP和端口号
- 如何找到主机后可以可靠的进行数据传输 -----> 网络协议:TCP/IP
IP:
- IP唯一标识一台主机,在java中用InetAddress表示IP地址。
- InetAddress:
- 实例化:getByName(String host) 、 getLocalHost()
- 常用的方法:getHostName() 、 getHostAddress()
端口号:
- 在计算机中运行的进行编号,不同进程不同的端口号,0~65535
IP+端口号 = Socket
TCP协议:
- 通信前进行TCP连接,三次握手,保证可靠通信
- 连接成功后可进行稳定的数据传输
- 传输完成后四次挥手,断开连接
- TCP特点:可靠性高、效率低
UDP协议:
- 将数据、源、目的封装成数据包、不需要建立连接
- 每个数据报大小为64K以内
- 可以进行广播发送,是否发送成功,发送方不知道。
- UDP特点:无需释放资源,速度快,开销小,可靠性低
2、TCP网络编程
这里用康师傅的原生代码!!!
//客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getByName("192.168.14.100");
socket = new Socket(inet,8899);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("你好,我是客户端mm".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源的关闭
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//服务端
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建服务器端的ServerSocket,指明自己的端口号
ss = new ServerSocket(8899);
//2.调用accept()表示接收来自于客户端的socket
socket = ss.accept();
//3.获取输入流
is = socket.getInputStream();
//不建议这样写,可能会乱码
// byte[] buffer = new byte[1024];
// int len;
// while((len = is.read(buffer)) != -1){
// String str = new String(buffer,0,len);
// System.out.print(str);
// }
//4.读取输入流中的数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while((len = is.read(buffer)) != -1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(baos != null){
//5.关闭资源
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3、UDP网络编程
//发送端
@Test
public void sender() throws IOException {
DatagramSocket socket = new DatagramSocket();
String str = "我是UDP方式发送的导弹";
byte[] data = str.getBytes();
InetAddress inet = InetAddress.getLocalHost();
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
socket.send(packet);
socket.close();
}
//接收端
@Test
public void receiver() throws IOException {
DatagramSocket socket = new DatagramSocket(9090);
byte[] buffer = new byte[100];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));
socket.close();
}
4、URL编程
- URL(Uniform Resource Locator):统一资源定位符,对应着互联网的某一资源地址
- url结构: http://localhost:8080/llj/index.html?username=Tom
协议 主机名 端口号 资源地址 参数列表
- url对象常用的方法:
public static void main(String[] args) {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL("http://localhost:8080/examples/1.jpg");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
is = urlConnection.getInputStream();
fos = new FileOutputStream("llj\\1.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
System.out.println("下载完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(urlConnection != null){
urlConnection.disconnect();
}
}
}