String,StringBuffer与StringBuilder的区别
1.首先在执行速度上 StringBuilder>StringBuffer>String
原因如下:String为字符串常量,StringBuilder和StringBuffer均为字符串变量。String对象创建之后不可更改,后面两种对象是变量,可以更改。
例如:String str="123"; str+="456";
这句代码,首先JVM创建一个新的String对象 str,把“123”赋值给str,接下来,JVM会再创建一个新的对象str,把原来str的值和“456”加起来再赋值给新的str,原来的str就被垃圾回收了。
注意:利用String方法进行大量数据操作时会出现堆内存溢出错误。
StringBuilder和StringBuffer有append()方法添加任意类型的字符串。
2.StringBuilder和StringBuffer之间的区别
StringBuilder线程上不安全的,StringBffer线程上是安全的。
StringBuffer很多方法都带有synchronized关键字,则它的线程是安全的,但是StringBuilder则没有这些关键字,所以不能保证线程安全。
3.总结
1)操作少量数据则使用String
2)单线程下操作大量数据用StringBuilder。
3)多线程下操作大量数据使用StringBuffer。
System类中的一个常用方法
System类中的静态方法Long currentTimeMillis();
获取当前系统时间与1970年01月01日00:00点之间的毫秒差值。使用方式为: System.currentTimeMillis();
通常用来计算程序所消耗的时间差值。
日期与日历
Date类的两个构造函数:
Date()
分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date)
分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
Date类当中很多方法都被DateFormat和Calendar所取代,因此以下为DateFormat和Calendar两个类的学习笔记。
DateFormat类是日期/时间格式化子类的抽象类,我们可以通过这个类帮我们完成Date对象和String对象之间的转换。分为格式化和解析两种转换方式。
常用规则为:y —-> 年,M —-> 月,d —-> 日,H —-> 时,m —-> 分,s —-> 秒
1 | public static void main(String[] args) { |
1 | public static void main(String[] args) throws ParseException { |
Calendar类是日历类,替换了Date类当中的很多方法,把时间信息封装为静态成员变量,方便获取各个时间的属性值。利用Calendar.getInstance()方法获取默认时区和语言环境的一个日历。
YEAR —-> 年,MONTH —-> 月(月份为0-11,可以+1使用),DAY_OF_MONTH —-> 月中的天(几号),HOUR —-> 时(12小时制),HOUR_OF_DAY —-> 时(24小时制),MINUTE —-> 分,SECOND —-> 秒,DAY_OF_WEEK —-> 周中的天(周几,周日为1,可以-1使用)。
1 | public static void main(String[] args) { |
集合之Collection集合
集合是用来存储多个数据的容器。它的长度是可变的,并不固定。集合存储的都是对象,当有多个对象时,使用集合存储。
1.Collection集合两大子接口之List接口
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。该接口有索引,并且可以存储重复元素。
List集合的两个常用实现类——–ArrayList,它具有元素增删慢,查找快的特点,所以日常开发中查找、遍历比较多的情况下使用ArrayList比较合适。
List集合的两个常用实现类——–LinkedList,它具有元素查找慢,增删快的特点,并且它提供了大量首尾操作的方法,因而在增删比较多的时候使用LinkedList。
2.Collection集合两大子接口之Set接口
一个不包含重复元素的 collection。此接口无索引,并且不能存储重复元素。当存储自定义类的时候,为了去重则需要重写equals和hashCode方法。
Set接口的常用实现类———HashSet,此类实现 Set 接口,由哈希表支持。不能存储重复元素,并且存储无序(即存储的和取出的顺序有可能不一致)。并且查询速度比较快。若要实现存取顺序一致,可以使用LinkedHashSet.
3.Collection集合的遍历
1.迭代器遍历:当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
1 | public static void main(String[] args) { |
注意:在遍历的过程中,如果想对集合进行数据的增删必须使用Iterator对象所有的增删方法,直接修改原集合会产生并发修改异常。
2.增强for遍历:增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
1 | public static void main(String[] args) { |
3.如果是List接口的实现类,还可以通过普通for循环利用索引来遍历数据。
4.泛型
泛型是可以在类或方法中预支地使用未知的类型。泛型的通配符为<?>,
泛型的高级使用:类型名称 <? extends 类 > 对象名称 ——> 只能接收该类型及其子类
类型名称 <? super 类 > 对象名称 ——> 只能接收该类型及其父类型
5.外比较器与内比较器
内比较器实现Comparable接口泛型为实现类的本类类型或其父类类型,compareTo方法一个参数,this与这个参数之间进行数据比较。
1 | public class Student implements Comparable<Student>{ |
外比较器实现Comparator接口泛型为实现类的本类类型或其父类类型,实现compare方法两个参数之间进行数据比较。
1 | public class MyComparator implements Comparator<Student> { |
集合之Map集合
public interface Map<K,V> 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射一个值。这个集合存储的是键值对,它的键不允许重复,值可以重复,键对值是一一对应关系。
1.Map常用的实现类有HashMap和TreeMap。
接下来主要说一下HashMap。HashMap是基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和 null键。它不能保证存取顺序一致(即存的数据和取的数据有可能不一致)。若想实现存储一致,可以使用LinkedHashMap。
1 | public static void main(String[] args) { |
2.Map集合的两种遍历方法
1 | 1.使用keySet方法遍历 |
异常的处理
1.异常处理的第一种方式throw
throw用在方法内,用来抛出一个异常,将这个异常传递到调用者处,并结束当前方法的执行。
使用格式:
1 | throw new 异常类名(参数); |
例如:
1 | throw new ArrayIndexOutOfBoundsException("哥们!你越界了"); |
2.异常处理的第二种方式throws
throws用在方法声明之上,表示不处理异常,交给方法的调用者来处理异常(抛出异常)。
使用格式:
1 | 修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ } |
例如:
1 | public static void readFile() throws FileNotFoundException,IOException {...} |
3.异常处理的第三种方式try…catch…finally
try…catch…finally方法可以对Java中异常有针对性的语句进行捕获,可以对出现的指定方式进行处理。try代码块中编写可能出现异常的代码,catch用来进行某种异常的捕获,实现对捕获异常的处理。finally代码块当中无论异常是否发生,都需要执行。
使用格式:
1 | try{ |
例如:
1 | public static void main(String[] args) { |
注意:当catch语句中异常为父子关系时,父异常必须放在子异常的下面。
线程
线程是分时调度原则:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。还有抢占式调度原则:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。Java在微观上使用的是并发执行多线程。
1.线程的两种创建方式——-继承Thread类
1 | public class MyThread extends Thread { |
2.线程的两种创建方式——-实现Runnable接口,把实现类对象作为参数传入Thread构造方法中。
1 | public class MyRunnable implements Runnable { |
3.继承Thread和实现Runnable之间的区别
实现Runnable接口比继承Thread类所具有的优势:
1.适合多个相同的程序代码的线程去共享同一个资源。
2.可以避免java中的单继承的局限性。
3.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4.线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
4.线程同步—–synchronized关键字和Lock锁
1.synchronized同步代码块
1 | synchronized(同步锁){ |
注意:同步锁对象可以是任意类型,多个线程对象要使用同一把锁。
1 | //售票案例 |
2.synchronized同步方法
1 | public synchronized void method(){ |
1 | //售票案例 |
3.Lock锁
同步代码块/同步方法具有的功能Lock都有,Lock更加的面向对象。使用Lock lock = new ReentrantLock();
获取Lock对象,加同步锁用lock()方法,释放同步锁用unlock()方法。
1 | //售票案例 |
5.线程等待唤醒机制
在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。wait方法与notify方法必须要由同一个锁对象调用。
经典案例——-生产者与消费者
包子类:
1 | public class BaoZi { |
包子铺类:
1 | public class BaoZiPu extends Thread { |
吃货类:
1 | public class ChiHuo extends Thread { |
测试类:
1 | public class Demo { |
执行结果:
1 | 正在做薄皮三鲜馅包子 |
6.线程池
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。使用Executors工具类的newFixedThreadPool(nThreads)方法获取线程池对象,参数传入线程数。
1 | public class RunnableImpl implements Runnable { |
代码的优化——Lambda表达式
在运用匿名内部类的时候可以使用Lambda表达式,前提是使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法(函数式接口)。
函数式接口(也可以包含静态,默认,私有的方法)接口之上可以加入注@FunctionalInterface,若接口不满足函数式接口时,该注解会有编译时异常。
使用Lambda必须具有上下文推断。Lambda表达式的格式如下:
1 | (参数类型 参数名称) -> { 代码语句 } |
在Lambda标准格式的基础上,使用省略写法的规则为:小括号内参数的类型可以省略;如果小括号内有且仅有一个参,则小括号可以省略;如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
1 | public static void main(String[] args) { |
四个常用的函数式接口
1.生产接口Supplier接口
该接口下包含一个无参数的方法T get(),用来获取一个泛型参数指定类型的对象数据。
2.消费接口Consumer接口
包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
默认方法andThen()参数和返回值都属Consumer类型,可以实现:首先消费一次,再消费一次,实现组合。
3.判断接口Predicate接口
包含抽象方法boolean test(T t).用于判断返回一个boolean类型的值。
默认方法and()起到了逻辑关系并且的作用。
默认方法or():起到了逻辑关系或的作用。
默认方法negate():起到了逻辑关系非的作用。
4.转换接口Function接口
Function
R apply(T t),根据类型T的参数获取类型R的结果。
默认方法andThen():一个拼接的作用,需要注意的是:当第一次调用apply方法更改参数类型之后,andThen括号内的对象第二次调用时apply()传入的类型为前一次转换之后类型的结果。类似与流水线。
I/O流
1.File类及其递归遍历
File类是文件和目录路径的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
三个常用构造方法:
public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例。
File类的常用方法查询api可以得到。不再一一举例,下面为File的递归遍历。
遍历有两种方法,file.list()返回一个String数组,表示该File目录中的所有子文件或目录。file.listFiles(括号内可传入过滤器实现类对象)返回一个File数组,表示该File目录中的所有的子文件或目录。
1 | public class Demo01Filter { |
FileFilter:文件过滤器。实现FileFilter中的accept方法可对文件进行过滤,根据pathname将满足条件的文件、文件夹返回ture,不满足的返回false即可。
1 | public class FileFilterImpl implements FileFilter { |
2.文件字节流输出流FileOutputStream(写入数据 由内存写出数据到其他设备)
1.构造方法
FileOutputStream(File file):构造方法传入File对象表示的文件创建文件输出流
FileOutputStream(String name):构造方法传入指定名称表示的文件创建文件输出流
2.写出字节数据(单个字节、多个字节)
write(int b):每次写出一个字节的数据
write(byte[] b):写出一个字节数组中的数据
write(byte[] b, int off, int len):从索引off开始,len个字节
3.数据追加、写出换行
数据追加即在构造方法中的第二个参数设置为true即可,换行符号为 \r\n 写出到文件即可。
4.close()方法关闭文件
为什么一定要调用close()方法关闭资源?因为如果不关闭资源,程序一旦无限循环时,资源文件就会一直打开无法关闭,我们就无法对文件进行删除等操作。
1 | public static void main(String[] args) throws IOException { |
3.文件字节流输入流FileInputStream(读取数据 由其他设备读取数据到内存)
1.构造方法
FileInputStream(File file):构造方法传入File对象表示的文件创建文件输入流
FileInputStream(String name):构造方法传入指定名称表示的文件创建文件输入流
2.读取字节
read():每次读取一个字节的数据,提升为int类型返回(例如读取文件中字符a的时候,返回97),读取到末尾时返回-1;
read(byte[] b):每次读取b的长度个字节到数组中,方法返回的是读取字节的个数,读取到末尾时返回-1;
3.close()方法关闭文件
1 | public static void main(String[] args) throws IOException { |
4.字节流之间的图片复制
首先用字节输入流从硬盘中读取一张图片,再利用字节输出流把读到的信息写出到另一个文件,达到了复制图片的效果。
1 | public static void main(String[] args) throws IOException { |
5.字符输出流FileWiter
字符流的本质是字节流+编码集(IDEA为UTF-8)。
1.构造方法
FileWiter(File file):创建一个新的FileWriter,给定要读取的File对象。
FileWriter(String fileName):创建一个新的FileWriter,给定要读取的文件路径。
2.写出字符数据
write(int b):每次写出一个字符数据。
write(char[] cbuf):每次写出一个字符数组的数据。
write(char[] cbuf, int off , int len):从索引off开始写len个字符
write(String str)和write(String str, int off, int len):每次可以写出字符串当中的数据。
3.关闭和刷新
由于每次调用write方法会先把数据写入内存缓冲区,不关闭输出流,无法写字符到文件中。但是关闭数据流无法继续写数据,此时需要用到flush()方法:刷新缓冲区,把字符写入文件。close()方法:先刷新缓冲区,再释放资源。
1 | public static void main(String[] args) throws IOException { |
6.字符输入流FileReader
1.构造方法
FileReader(File file):创建一个新的FileReader,给定要读取的File对象。
FileReader(String fileName):创建一个新的FileReader,给定要读取的文件路径。
2.读取字符数据
read():每次读取一个字符的数据,提升为int类型,返回回来,读取到文件的末尾返回-1。
read(char[] cbuf),每次读取cbuf的长度个字符到数组中,返回读取的有效字符个数,读取到末尾时返回-1。
3.关闭资源
1 | public static void main(String[] args) throws IOException { |
7.IO异常处理JDK7/JDK9新特性
1.JDK7的处理
把创建字符/字节的输出/输入流语句放入try()中,try-catch模块执行完自动释放资源。不用手动的close()。
1 | try (创建流对象语句,如果多个,使用';'隔开) { |
1 | public static void main(String[] args) { |
2.JDK9的处理
把字符/字节的输出/输入流对象放入try()中,try-catch模块执行完自动释放资源。
1 | public static void main(String[] args) throws IOException { |
8.Properties类
继承于Hashtable,是一个集合,存储的键值都是字符串类型。
setProperty(String key, String value):存储数据。
getProperty(String key):根据键找值。
Set
load(InputStream inStream):从字节输入流读取键值对(键与值之间可以是‘=’、‘ ’,注释用#)。
load(Reader reader):从字符输入流读取键值对(内容可以包含中文,字节流不可以)。
p.store(FileOutputStream outStream, String comments):从字节输出流写出键值对。
p.store(Writer witer, String comments):从字符输出流写出键值对(内容可以包含中文,字节流不可以)。
1 | public static void main(String[] args) throws IOException { |
9.缓冲流
缓冲流,也叫做高效流、包装流,是对四个基本流的增强,创建流对象时会创建一个内置默认大小的缓冲区数组,通过缓冲区读写,减少系统的IO次数,从而提高读写的效率。
1.字节缓冲流
public BufferedInputStream(InputStream in):传入一个字节输入流,创建出一个新的缓冲输入流。
public BufferedOutputStream(OutputStream out):传入一个字节输出流,创建出一个新的缓冲输出流。缓冲字节输出流也需要flush()方法。
缓冲字节流基本方法和普通字节流一致。
2.字符缓冲流
public BufferedReader(Reader in):传入一个字符输入流,创建出一个新的缓冲输入流。
public BufferedWriter(Writer out):传入一个字节输出流,创建出一个新的缓冲输出流。缓冲字符输出流也需要flush()方法。
缓冲字符流基本方法和普通字符流一致。
BufferedReader里面包含一个特有方法:readLine():一次读取一行文字,返回一个字符串。但是读不到换行符。
BufferedWriter里面包含的特有方法:newLine():写一个换行符,此方法可以跨平台使用。
缓冲流文件复制案例:
1 | public static void main(String[] args) throws IOException { |
10.转换流
转换流是用普通字节流加上字符集组成。
InputSreamReader(InputStream in,String charsetName):创建一个指定字符集的转换输入字符流,第二个参数不写会使用默认字符集(例如IDEA使用utf-8字符集)。
OutputStreamWriter(OutputStream out,String charsetName):创建一个指定字符集的转换输出字符流,第二个参数不写会使用默认字符集(例如IDEA使用utf-8字符集)。
案例:读取GBK文件,写出UTF-8文件
1 | public static void main(String[] args) throws IOException { |
11.序列化及反序列化
1.序列化:用一个字节序列表示一个对象,将该对象信息写出到文件。
ObjectOutputStream(OutputStream out)将Java对象的原始数据类型写出到文件,实现对象的持久存储。构造方法内传入一个字节输出流对象。
一个对象(例如:Student类对象)要想序列化,必须实现Serializable接口作为可以被序列化的标记,另外此类要重写serialVersionUID这个参数,避免该类修改序列号改变引起异常。
若是某个属性不想被序列化,可以加transient关键词修饰。可避免被序列化。
调用writeObject(Object obj)方法写出指定对象。
2.反序列化:用该字节序列从文件中读取出来,重构对象。
ObjectInputStream(InputStream in):构造传入一个字节输入流对象。
调用readObject()方法读取一个对象,如果想使用该对象的特有方法,我们则需要向下强转。
由于读取对象没有结束标志,读完会产生一个异常,所以有多个对象时我们可以把它们写在一个集合当中,此时序列化时,直接将集合对象写出到文件当中,这样就可以写一次、读一次避免异常。
序列化集合案例:
1 | Person类: |
12.打印流PrintStream
打印流的好处:可以使用print、println方法打印,后者自带换行。在构造方法可以指定编码集,自动刷新。打印的数据类型都保持原样。
PrintStream(String fileName):构造方法中传入文件路径。
System.setOut(PrintStream ps):可以改变打印流的流向,把输出到控制台的信息直接输出在文件里。
1 | public static void main(String[] args) throws FileNotFoundException { |
网络编程
Tcp协议的三次握手:
第一次握手,客户端向服务器端发出连接请求,等待服务器请求。
第二次握手,服务端向客户端回送一个回应,通知客户端收到了连接请求。
第三次握手,客户端再次向服务器端发送确认信息,确认连接。
1.Socket类
public Socket(String host, int port):创建套接字对象并将其连接到主机上的指定端口号。如果制定的host是null,则相当于指定地址为回环地址(127.0.0.1).
host指定IP地址,port指定端口号。
getInputStream():返回此套接字的输入流。
getOutputStream():返回此套接字的输出流。
close():关闭套接字。
shutdownOutput():禁用此套接字的输出流。可以理解为写出了结束标志。
2.ServerSocket类
public ServerSocket(int port):使用该方法创建ServerSocket对象时,将其绑定到指定端口号上。
public Socket accept():侦听并接受连接,返回一个Socket对象,用于和客户端实现通信,该方法会一直阻塞直到建立连接。
客户端向服务端上传图片案例:服务器端使用无线循环和多线程,能多次接收上传减少耗时。
1 | Runnable的实现类: |
服务端和客户端实现实时聊天:客户端和服务端都采取了多线程,把读取的数据和写出的数据分别无线循环达到了实时发送、接收消息的目的。
1 | 服务器端代码: |
Stream流
Stream是Java 8新加入的最常用的接口。元素是特定类型的对象,形成一个队列。Stream并不会存储元素,而是按需计算,流的来源可以是数组,集合等。Stream流提供了内部迭代方式,流可以直接调用最终方法。
1.获取Stream流
Collection获取流:用.stream()获取流。
Map获取流:先转换为keySet()和entrySet(),然后再.stream()获取流。
数组获取流:用Sream.of()方法获取流。
2.常用方法
延迟方法:过滤方法filter,此方法参数传入一个判断接口(Predicate)的Lambda表达式,返回true保留,返回false则过滤。
映射方法map,此方法传入一个转换接口(Function)的Lambda表达式,将stream流当中的格式转换。
截取方法limit(n),传入一个长度,只取用前n个(包括n),如果集合当前长度大于参数则进行截取;否则不进行操作。
跳过方法skip(n),跳过前n个元素(包括n)。
组合方法concat(),将两个流合并为一个流。
终结方法:逐一处理方法foreach,方法内传入一个消费接口(Consumer)的Lambda表达式,一般用于集合数据打印。
统计个数方法count(),计算流中的元素个数。
存储到集合collect(Collectors.toList()),将流中的数据存储到集合里。
存储到数组toArray(),将流中的数据存储到数组里。
反射
1.获取Class对象的方式
1 | 1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象 |
2.Class对象功能
1 | 获取功能: |