java SE


---day 01 String----------

目录

---day 01 String----------

1.文档注释可以在三个地方使用:类,方法,常量
 * 在类上使用,用于说明当前类的设计目的。
2.0 String是不变对象(字符串是常量),即:字符串对象一旦创建,内容不可改变,想改变内容一定创建新对象
Java建议:创建字符串对象时使用字面量形式直接赋值。
因为,这样创建时Java会重用已经创建过的字符串对象。
使用字面量创建字符串对象时,JVM会首先检查常量池是否已经缓存过该内容的字符串对象,若有,则直接重用该对象,若没有才创建该字符串对象并缓存它。
//字面量形式直接赋值:String s1="hello";
//修改内容会创建新对象:s1+="WW";
编译器有一个优化措施:当编译器在编译代码时,发现一个计算表达式的参数都是字面量时,那么会直接计算,并将结果编译到class文件中。
所以:String s5="123"+"abc"会被编译器改为=String s5="123abc"

频繁修改字符串带来性能损耗。

2.由于字符串的设计及优化是针对"重用性",所以字符串不适合频繁修改。
Java设计了一个专门针对字符串修改操作的类:
StringBuilder,内部维护一个可变的字符数组,使用它修改字符串方便,并且开销小。
StringBuilder sb=new StringBuilder();
 1) StringBuilder sb.append("你好");//追加内容
 2)StringBuilder replace(int start,int end,String str)
//start-起始索引,end-结束索引,str-将"替换"原有内容的字符串
 3)StringBuilder delete(int start, int end) 
          "移除"此序列的子字符串中的字符。
 4)StringBuilder    insert(int offset, "") 
          将字符串表示形式"插入"此序列中。

3.String API

 1)char charAt(int index)
返回当前字符串中给定位置对应的字符
 2)int indexOf(String str)
查找给定字符串在当前字符串中的位置。若当前字符串中不含有该内容则返回-1.
 int indexOf(int ch, int fromIndex) 
返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。 
int lastIndexOf(String str, int fromIndex) 
返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
 3)boolean startsWith(String str)
    boolean endsWidth(String str)
判断字符串是否是以给定的字符串开始或结束的。
 4)String substring(int start,int end)
截取当前字符串指定范围的字符串。
需要注意,java API有个特点,通常使用两个数字表示范围时,都是“含头不含尾”的。
 5)String toUpperCase()
    String toLowerCase()
 将当前字符串中的英文部分转换为全大写或全小写
 6)String trim()
去除当前字符串两边的空白字符
  7)String提供了一组静态的valueOf方法,作用是将其他类型数据转换为字符串。
String.valueOf(234);返回多种参数的字符串表示形式

String方法:
int length()
返回当前字符串长度(字符个数)


-----day02------------


1.正则表达式
\d //数字: [0-9]
\w //单词字符:[a-zA-Z_0-9]
\s //空白字符
X?    X,一次或一次也没有
X*    X,零次或多次
X+    X,一次或多次
X{n}    X,恰好 n 次
X{n,}    X,至少 n 次
X{n,m}    X,至少 n 次,但是不超过 m 次
分组“()” 可以将一系列正则表达式看做一个整体,分组时可以使用“|”表示“或”关系
  1)字符串支持正则表达式方法一:
 boolean matches(String regex)
 使用给定的正则表达式验证当前字符串是否满足格式要求
注意:无论正则表达式是否加入了边界匹配符:"^","$"都是全匹配验证
 2)字符串支持正则表达式方法二:
     String[] split(String regex)
 按照满足给定的正则表达式的部分进行拆分当前字符串,
 将拆分后的每部分以数组的形式返回。
 3)字符串支持正则表达式方法三:
 String replaceAll(String regex,String str)
 将当前字符串中满足正则表达式的部分替换为给定字符串

2.Object
 Java中所有的类都继承自Object
 当一个类没有使用extends标明继承某个类时,默认就继承自Object
 (编译器会在编译该类时补充:extends Object)
 1)Object常用的方法一: String toString()
重写Object的toString方法。返回的字符串格式上没有要求,
按照将来实际开发需求而定。原则上应当包含当前对象的有效信息(属性值)
 * 该方法的意义是将当前对象转换为一个字符串,
 * Object实现的该方法返回的字符串内容为当前对象的句柄(地址),实际意义不大。
 * 通常我们使用一个对象的toString方法时应该重写它,返回的字符串应当包含当前对象有效信息。Java的api很多情况下也会调用toString方法。

  2)boolean equals(Object obj)
通常我们需要使用一个对象的equals方法就应当重写它
(Java提供的类不需要,已经重写好了)
重写equals的目的是比较两个对象this和参数obj的内容是否相同。
而比较也并非要求必须所有属性值都相同。
可结合实际开发需求而定。

3.Integer包装类
由于Java有8个基本类型,它们不具有面向对象的特性,
那么在实际开发中不能直接参与面向对象的开发,这很不方便
为此,Java为8个基本类型提供了对应的包装类。
目的是可以让基本类型以对象的形式存在,能够参与到面向对象的开发中
 
6个表示数字类型的包装类,他们都继承自Java.lang.Number
Number是一个抽象类,提供了在6种类型之间转换数字的相关方法。
/*Integer i=Integer.valueOf(33)
  Integer i2=Integer.valueOf(33)
-128~127之间,不用重新在new Integer()
*/
 数字类型的包装类都提供了两个常量:
MAX_VALUE,MIN_VALUE
用来获取其表示的基本类型的取值范围

包装类提供了一个非常实用的静态方法:parseXXX(String str)
  可以将字符串转换为对应的基本类型
包装类将字符串转换为基本类型的前提条件是
该字符串要正确描述基本类型可以保存的值。

 * 自JDK1.5之后,推出了一个特性:自动拆装箱
 * 该特性是编译器认可,而不是虚拟机。编译器在编译源程序
 * 时若发现有基本类型和包装类互相赋值使用时会自动补全它们
 * 之间的转换代码。

 触发自动拆箱特性:
编译器会将代码改变为:
int d = new Integer(123).intValue();
触发自动装箱特性:
编译器会将代码改变为:
Integer in = Integer.valueOf(123);

-----day 03------------
1.File
java.io.File
File的每一个实例用于表示文件系统(硬盘中)上的一个文件或目录。
使用File可以:
 1):访问属性信息(名字,长度,修改时间等)
 2):操作文件或目录(创建,删除)
 3):获取一个目录的子项
但是不能访问文件数据。

File没有无参构造方法,需要传入一个字符串,该字符串内容为路径。
注意,路径通常不会使用绝对路径,因为不同系统的绝对路径定义不同,会出现跨平台问题。
想对路径的好处再与不存在跨平台问题,但是相对的路径通常由运行环境而定。实际开发中比较常用的是"类加载路径"。
在eclipse中,通常想对“当前目录”,当前目录指的是当前程序所在项目的项目目录。

/*
File file=new File("demo.txt");//默认当前目录下的demo.txt
file.getName()//获取名字
file.length();//获取文件的长度(字节量)
boolean  :
   file.canRead()//可读
   file.canWrite()//可写
   file.isHidden()//是否隐藏
  file.exists();//判断文件是否存在
  file.createNewFile();//将当前File表示的文件创建出来
  file.delete();//删除文件
  file.mkdir();//创建一个目录。父目录必须存在才能创建
  file.mkdirs();//创建多层目录。可以将父目录一同创建出来
*/
过滤器??
--==


java.io.RandomAccessFile (随机访问文件)
RAF是专门用来读写文件数据的类,它基于指针进行读写。
可对文件内容进行编辑操作。
其构造器还需要第二个参数,用来指示我们只是"随机读"(r)还是"既读又写"(rw)

方法:
void write(int d)
向文件中"写入一个字节,将给定的int值所对应的2进制的低8位写入文件"

读取一个字节
int read()
从文件当前指针位置读取1个字节,读取后以int形式返回。若读取到文件末尾,则返回值为-1
/*
//复制文件
创建一个RAF读取源文件
创建一个RAF向复制文件中写
顺序的从源文件读取每个字节写入到复制的文件中
RandomAccessFile src = new RandomAccessFile("music.mp3","r");
RandomAccessFile desc= new RandomAccessFile("music_cp.mp3","rw");    
//用来保存每次从原文件读取到的字节
int d = -1;    
    while((d = src.read()) != -1) {
        desc.write(d);
    }
 */

-----day 04--------------
通过提高每次读写的数据量,减少实际读写的次数,可以提高:读写效率
读写有两种:
    "随机读写":单字节读写(raf.read())
    "块读写":一组一组字节读写(raf.read(byte[] b))

RAF提供的块读写操作方法
块读:
int read(byte[] data)
 将最多 data.length 个数据字节从此文件读入 byte 数组。"返回值为实际读取到的字节量"。
若返回值为-1,则表示本次没有读取到任何数据(已经时文件末尾了)
块写:
void write(byte[] data)
将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
void write(byte[] data,int offset,int len)
将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 offset 处开始。
len是每次实际读取的字节量
code::::
//记录每次实际读取到的字节量
int len=-1;
byte[] b=new byte[1024*10];
while((len=src.read(b))!=-1) {
    desc.write(b, 0, len);
}

向文件中写入字符串

字符串提供了转换为字节的方法:
byte[] getBytes()
将当前字符串按照系统默认字符集转换为一组字节
byte[] getBytes(String csn)
使用指定的字符集将字符串转换为对应的一组字节
常见字符集:
GBK:国际编码,英文1字节,中文2字节
UTF-8:unicode的一种字符集,也称为万国码,包含世界上流行的语言对应的字符。
英文1字节,中文3字节
ISO8859-1:欧洲的一种字符集,不支持中文。
//将给定的字节数组中所有字节按照指定字符集还原为字符串
String str=new String(data, "GBK");

RAF提供了读写基本类型数据的相关方法。以及操作指针的相关方法
RAF基于指针进行读写数据的,即:RAF总是"在指针指向的文件字节位置进行读或写",
并且读写后指针会自动向后移动到写一个字节准备读写。
int的最大值2^31-1                       vvvvvvvv
01111111 11111111 11111111 11111111
位操作符
>>> 将二进制整体向右移动指定位数
d>>>24                    vvvvvvvv
00000000 00000000 00000000 01111111 11111111 11111111 11111111
d>>>16                    vvvvvvvv
00000000 00000000  01111111 11111111 11111111 11111111

int d=Integer.MAX_VALUE;

raf.write(d>>>24);
raf.write(d>>16);
raf.write(d>>8);
raf.write(d);
// RAF提供了方便直接写出基本类型数据的相关方法:
raf.writeInt(d);//连续写出4个字节,将该int值写出,若读取过程中发现到了文件末尾,则抛出EOFException
//一次性写出8字节,将long值写出
raf.writeLong(234L);

//将指针移动到指定位置
raf.seek(0);
raf.getFilePointer()//获取当前RAF的指针位置

"RandomAccessFile"适用于大小已知的记录组成的文件,所以我们可以使用seek()将记录从一处转移到另一处,然后读取或者修改记录。文件中记录的大小不一定都相同,只要我们能够确定那些记录有多大以及它们在文件中的位置即可。
它是一个完全独立的类,从头开始编写其所有的方法(大多数都是本地的)。这么做是因为RandomAccessFile拥有和别的I/O类型本质不同的行为,因为我们可以在一个文件内向前和向后移动。在任何情况下,它都是自我独立的,直接从Object派生而来的。
从本质上说,RandomAccessFile的工作方式类似于把DataInputStream和DataOutputStream组合起来使用,还添加了一些方法。其中"方法getFilePointer()用于查找当前所处的文件位置,seek()用于在文件内移至新的位置,length()用于判断文件的最大尺寸"。另外,其构造器还需要第二个参数,用来指示我们只是"随机读"(r)还是"既读又写"(rw)。它并不支持只写文件,这表明RandomAccessFile若是从DataInputStream继承而来也可能会运行得很好。
只有RandomAccessFile支持搜寻方法,并且只适用于文件。


-----day 05-----------
接口,定义规范,

Java标准IO
IO指的是输入与输出(Input,Output)
java将读写操作按照方向划分位输入流和输出流,
其中输入流用来读,输出流用来写。
java.io.InputStream:所有字节输入流的超类,定义了若干抽象方法,
其中规定了读取字节的相关操作。
java.io.outputStream:所有字节输出流的超类。定义了写出字节的相关方法。

流有两类:
"节点流(字节流)和处理流(字符流?)"

"节点流":又称为低级流。是实际链接程序与数据源的“管道”,负责实际数据的搬运工作。读写数据一定是建立在"节点流"的基础上进行的。特点:数据源明确。

"处理流":又称高级流。不能独立存在,必须链接在其他流上,作用是当数据流经该流时,其可以对数据进行某些加工处理。这样可以简化我们再对数据加工的操作。
"流的链接":将需要的一组处理流链接再一起,最终链接在节点流上,可对数据进行“流水线”式的加工并进行读写,这也是IO读写的重点。

文件流,文件流是一组"节点流",作用是读写文件。
文件流与RAF都是用来读写文件数据的,但是他们也有各自的优点与缺点。
文件流:由于流的读写模式是顺序读写,所以文件流也是以该方式读写文件数据的。所以做不到编辑文件数据(只覆盖文件特定位置的字节)。读写不能回退操作。但是基于流链接操作可以轻松读写复杂数据。

"RAF":基于指针进行读写,所以RAF可以操作指针读写特定位置的字节,能够编辑文件数据。虽然有提供方便的读写基本类型数据与字符串等方法,但是读写复杂数据需要自行完成。

文件输出流有两种创建方式:
FileOutputStream(File file)
FileOutputStream(String path)
以上构造方法创建的文件流是覆盖写操作,即:若该文件已经存在,会先将该文件数据清楚,然后通过这个流写出的内容作为文件数据保存。

FileOutputStream(File file,boolean append)
FileOutputStream(String path,boolean append)
若第二个参数为true,则是追加模式,即:若该文件存在,保留该文件所有数据,将通过当前流写出的数据

使用文件流复制文件
1:创建文件输入流读取原文件 FileInputStream
2:创建文件输出流向复制文件写 FileOutputStream
3:顺序从原文件读取字节写入复制文件中,完成复制操作

"缓冲流"
java.io.BufferedOutputStream
java.io.BufferedInputStream
缓冲流是一对"高级流",可以加快读写数据的效率。无论我们通过缓冲流进行的是随机读写还是块读写,最终都会被缓冲流转换为"块读写"操作。
缓冲输出流的缓冲问题
void flush()
缓冲流提供了强制清空缓冲区的操作,调用flush会将当前缓冲区已经缓冲的字节一次性写出。

---
使用Person类的实例测试对象流的读写操作
"java.io.Serializable接口"
当一个类的实例希望被对象流读写,那么该类必须实现:java.io.Serializable接口,否则在序列化时会抛出异常。

当我们实现Serializable接口后,该类被编译器编译时会多出一个方法,作用是将当前类实例转换为一组字节。但是源码中不体现。
由于不实现该接口,编译器不会给类添加这个方法,这就是为什么对象流在序列化时要求必须实现该接口的原因。否则无法将对象转换为一组字节。
--
当一个类实现了Serializable接口后,应当定义一个常量:serialVersionUID(序列化版本号)
序列化版本号直接影响反序列化对象的结果是否成功。

当对象流输入反序列化一个对象时,会检测该对象的版本号是否与当前版本号一致,若不一致则直接抛出"反序列化失败异常"。若版本号一致,则采用兼容模式,即:若反序列化的对象与当前类结构不完全一致时,所有可以还原的属性都还原。若结构一致则直接还原。

"transient"
当一个属性被transient修饰后,那么当前类实例在序列化时,该属性值会被忽略。忽略不需要保存的属性可以达到对象瘦身的功能。(Person类中的属性)
private transient String[] info;

"对象流"
java.io.ObjectOutputStream
java.io.ObjectInputStream
对象流是一对高级流,可以方便的读写java中任何对象。
ObjectOutputStream oos = new ObjectOutputStream(fos);
对象输出流提供了写出对象的方法:
 void writeObject(Object obj)
将给定的对象转换为一组字节后写出
这里通过oos给定对象写入文件,实际经历了两个操作:
    1:oos将给定的对象转换为了一组字节,这个过程称为:"对象序列化"
    2:fos将这组字节写入到文件中(硬盘上),这个过程称为:"数据持久化"

"对象反序列化"
ObjectInputStream
对象输入流提供了对象反序列化的方法:
    Object readObject()
需要注意,该方法在读取字节时,必须保证读取到字节时对象输出流将一个对象序列化的字节,否则会抛出ClassNotFoundException
---

----day 06-----------


 "字符流"
java按照读写单位将流划分为:"字节流和字符流"
字符流的读写单位是以字符为单位的。但底层实际还是要转换为字节进行写操作,
或者将读取到的字节转换为字符。本质还是读写字节。
字节流操作的是字节数组;字符流操作的是字符数组。

字符流原理

Reader是字符输入流的父类
Writer是字符输出流的父类
字符流是以字符(char)为单位读写数据的。一次处理一个unicode
字符流的底层仍然是基本的字节流
字符流封装了字符的编码解码算法。

Reader的常用方法:
int read():读取一个字符,返回的int值“ 低16”位有效。
===
java.io.BufferedReader
缓冲字符输入流,可以按行读取字符串
    String readLine()
顺序读取若干字符,直到读取了换行符为止,然后将换行符之前的所有字符以一个字符串形式返回。注意:返回的字符串中不含有最后的换行符的。
若返回值为null,表示末尾。

 "(字符)转换流":
java.io.OutputStreamWriter
java.io.InputStreamReader 读取字符
它们是字符流的一对常用实现类,将来实际开发中很少直接使用它们,但它们在流链接中是重要的一环。
字符转换流原理:字节流+编码表。


"缓冲字符流"
java.io.BufferedWriter
java.io.BufferedReader
缓冲字符流可以"块读写",并且特点是可以按行读写字符串

java.io.PrintWriter
具有"自动行刷新"的缓冲字符输出流。内部连接BufferedWriter作为缓冲功能。
在创建"PrintWriter"时,若构造方法第一个参数为流(字节流、字符流均可),那么该构造方法就支持一个重载,
可以再传入第二个参数,该参数为boolean型,若值为true时,则具有了自动行刷新的功能。即:每当调用println方法(注意:不是调用print方法!!!)后就会自动刷新,将该字符串实际写出。但是该功能会降低写的效率(因为提高了写的次数), 但是,若考虑到即时性时,该方法很易用。
PrintWriter pw = new PrintWriter(bw,true);//自动行刷新    
PrintWriter pw = new PrintWriter("pw.txt","UTF-8");//不具备    
===
 
FileOutputStream 
    将字节写入文件
OutputStreamWriter
    将字符转换为字节。"连接字符流与字节流"
BufferedWriter
    块写操作,提高写的效率
PrintWriter
    自动行刷新,按行写出字符串

//异常
Throwable是java.lang包中一个专门用来处理异常的类。它有两个子类,即Error和Exception,它们分别用来处理两组异常。
Error用来处理程序运行环境方面的异常,比如,虚拟机错误、内存空间不足、装载错误,这类异常主要是和硬件有关的,而不是由程序本身抛出的。
Exception分为运行时异常和非运行时异常,也称为不检查异常和检查异常。
运行时异常是由程序本身引起的,但不是程序主动抛出的,而是在程序运行中产生的。比如:空指针异常、字符串下标越界异常、数组下标越界异常、算数异常、输入不匹配异常、类造型异常
非运行时异常也称显示异常。它们都是在程序中用try...catch 捕获或者throws声明抛出,比如:文件没有找到引起的异常、类没有找到引起的异常等(IOException,SQLException)。

Error和RuntimeException的子类是unchecked的,也就是不需要明确地throws Error也能编译通过。


"异常"
JDK7新增功能-利用trycatch语句自动关闭资源
注意:jdk1.7中添加的try-with-resources语句来自动关闭资源。

try(
/*实现了AutoCloseable接口的类,在这里可以被自动关闭,实际上,下面的代码会被编译为之前代码的内容*/
FileOutputStream fos=new FileOutputStream("h.dat");
){
    fos.write(97);
}catch(Exception e){
    ...
}finally{
}
注意:1)try语句块中出错的代码以下的内容不会被执行
2)如果try语句块中的代码没有出现异常,那么catch语句块是不执行的
3)应当养成一个好习惯,在最后一个catch上捕获Exception,避免因为try代码块中一个未捕获的异常导致程序中断。


finally块
finally块只能定义在异常处理机制的最后。即:直接跟在try之后或者最后一个catch之后。
finally块可以保证只要程序执行到try语句块中,那么finally块中的代码必定执行。
所以,通常将程序是否出异常都要执行的代码放在这里,例如IO操作完毕后的流关闭。

"面试:简述final,finally,finalize"
1)fianl:可以作为修饰符修饰变量、方法和类,被fianl修饰的变量不能被改变,被final修饰分方法不能被重写,被final修饰的类不能被继承。
2)finally:是Java保证代码一定要被执行的一种机制。我们可以使用try-finally或者try-catch-finally来进行类似关闭JDBC连接、保证unlock锁等动作。
3)finalize是Object定义的方法。它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize机制现在已经不推荐使用,并且在JDK9开始被标记为deprecated。当GC要释放一个对象时会调用该方法。该方法执行完毕意味着其即将被释放。该方法不应当有耗时操作。
为什么呢?因为你无法保证finalize什么时候执行,执行的是否符合预期。使用不当会影响性能,导致程序死锁、挂起等。

1,finally语句。★★★★★
    1.1 解决什么问题?保证特定代码无论问题发生与否都执行。
    1.2 使用场景?用于关闭或者释放的资源的代码。

2,try catch finally 结合方式。★★★★★
    2.1 try catch : 检测异常,并捕获异常。
    2.2 try finally : 检测异常,不捕获,如果是编译时异常,必须throws声明,关闭资源。
    2.3 try catch finally : 检测异常,捕获异常,关闭资源。
        
3,异常在重写中的应用★★★★★
    3.1 子类重写父类,只能声明父类的异常或者该异常的子类以及子集,或不声明。
    3.2 父类没有声明异常,子类重写时也不可以声明,重写的方法中发生了异常,必须catch捕获,可以在catch处理,也可以 throw运行时异常。


"异常抛出相关知识"
使用throw在方法中抛出一个异常通常在以下情况发生:
1:符合语法,但是不符合业务逻辑时,可以当成异常抛出给调用者。
2:确实发生了一个异常,但是该异常不应当被当前方法解决,这时可以抛出给调用者。

通常方法中使用(throw)什么异常,就要在方法声明的同时使用throws声明该异常的抛出。
只有RuntimeException及其子类异常不需要抛出。

-----day 07----

 当调用一个含有throws声明异常抛出的方法时,必须处理该异常,否则编译不通过。
处理异常的方式有两种:
    1,使用try-catch捕获并处理异常
    2,在当前方法上继续使用throws声明该异常的抛出。

"throw和throws的区别"
1,throw用在方法内。throws用在方法上。
2,throw抛出的是异常对象。throws用于进行异常类的声明,后面异常类可以有多个,用逗号隔开。
void show() throws Exception{
    throw new Exception();
}
    
throws是用来在方法上声明该方法可能会抛出的异常,
要求调用者必须处理该异常。

常见的异常类型:
NullPointerException//空指针异常
StringIndexOutOfBoundsException//字符串下标越界
ArrayIndexOutOfBoundsException//数组下标越界
ArithmeticException//算数异常
InputMismatchException//输入不匹配异常
ClassCastException//类造型异常

 自定义异常
自定义异常通常用来说明如业务逻辑错误的情况

**自定义异常。重点看ExceptionDemo5.java(遇到问题解决问题的思路) .★★★★★
    3.1 为什么要自定义异常?
    3.2 自定义异常到底该继承谁?
    3.3 Exception和RuntimeException的区别?
        编译时异常,运行时异常。
    3.4 这两个异常到底什么时候用?
        问题发生时,需要调用者给出具体的处理方式吗?
            需要,编译时异常。
            不需要,运行时异常。
**异常的声明和捕获。★★★★★
    声明:throws。
        到底是否需要声明呢?通过调用者是否需要给出处理方式而定。
    捕获:try catch finally  catch才是真正的处理的异常的部分。没catch就没处理。

    声明和捕获到底用哪个呢?
    功能中如果可以解决问题用捕获,解决不了用声明。

**异常的部分细节:★★★★★★★
    1,一个功能可以声明多个异常。
    2,对于多个异常声明,必须对应多个catch针对性处理。
    3,catch中处理不了的异常可以继续抛出,也可以转换抛出。

"Socket"
socket 套结字
Socket封装了TCP协议的通讯细节,为我们提供了简洁的实现过程,通过操作两个流来完成与远端计算机之间的数据传输

socket=new Socket("localhost",8088);
实例化Socket时需要传入两个参数,并且实例化的过程就是与远端计算机建立连接的过程。
若远端计算机没有响应,会抛出异常。
参数1:远端计算机地址信息(IP)
参数2:远端计算机开启的端口(port)

OutputStream  out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
BufferedWriter bw=new BufferedWriter(osw);
PrintWriter pw=new PrintWriter(bw,true);
//写出发送给服务端
pw.println("你好服务端");

"ServerSocket"
java.net.ServerSocket
运行在服务端的ServerSocket主要有两个作用
1:向服务端所在系统申请一个服务端口,客户端就是通过这个端口与服务端建立连接的
2:监听该服务端口,一旦一个客户端通过该端口与服务端进行通讯,那么,ServerSocket就会自动的创建一个Socket与客户端进行通讯。

实例化ServerSocket的同时要指定向系统申请的服务端口。
server=new ServerSocket(8088);
注意:该端口不能与当前系统其他应用程序申请的端口号一致,否则会抛出端口被占用异常。

ServerSocket提供的方法:
Socket accept();
该方法是一个阻塞方法,调用后服务端开始监听服务端口,一旦一个客户端通过该端口请求连接,就会创建并返回一个Socket实例,通过这个Socket就可以与刚建立连接的这个客户端进行通讯了。

"关闭防火墙"
root
查看防火墙状态:firewall-cmd --state
显示running(开启状态)
关闭防火墙:systemctl stop firewalld.serice
查看防火墙状态:
显示not running(即成功关闭) 

systemctl disable firewall -service 开?

---day 08-----
进程???线程???
进程:进程是拥有资源的基本单位。
线程:线程是CPU调度的基本单位。
进程是由一个或者多个线程组成的。不同的进程之间资源一般不共享的。。。?
多进程与多线程有哪些区别呢 ? 本质的区别在于每个进程拥有自己的一整套变
量, 而线程则共享数据 。

多线程
多线程可以实现多段代码“同时运行”的效果。实际上是并发运行的。
 
"创建线程"有两种方式
方式1:继承Thread并重写run方法。run方法的作用是定义该线程要执行的任务。

Thread t2=new MyThread2();
t2.start();
启动线程要调用start方法,而不是直接调用run方法,
当一个线程的start方法调用完毕后,该线程会纳入到线程调度中,
一旦被分配CPU时间片,该线程会自动的执行其run方法开始运行任务。
所以,一个线程的start方法执行后,run方法会很快的被运行起来。

 第一种创建线程的方式(继承Thread)比较简单,适合使用匿名内部类快速创建临时线程跑任务。但是,它也存在两种设计不足
 1:由于需要继承Thread,而Java又是单继承的,这就导致了若继承了Thread就无法再继承其他类去复用方法。
 这在实际开发中是出现比较多的矛盾。
 2:继承Thread后需要重写run方法来定义任务,这就导致了线程与线程要执行的任务之间存在一个必然的耦合关系,
 导致线程重用性变差。

方式二:
实现Runnable接口,单独定义线程任务
Runnable r1=new MyRunnable();
Thread t1=new Thread(r1);
t1.start();

"线程状态(6种)"
New(新创建)
Runnable(可运行)
Blocked(被阻塞)
Waiting(等待)
Timed Waiting(计时等待)
Terminated(被终止)

Thread提供了一个静态方法
static Thread currentThread()
该方法可以获取到运行该方法的线程并将其返回。
后面会有一个很重要的API:ThreadLocal,就是利用这个方法解决问题的。
包括后期spring中使用aop管理事物时就用到了这个操作。
//获取运行main方法的线程(在main()中)
Thread main=Thread.currentThread();
//获取运行dosome方法的线程(在dosome()中)

 线程的优先级
线程在并发运行时对于线程调度的工作是不可控的,
即:不能主动获取CPU的时间片,只能被动的被分配。
但是线程调度会尽可能均匀的将时间片分配给所有并发运行的线程,
 但不能保证每个线程“一人一次”。
通过调整线程的优先级可以最大程度的改善获取CPU时间片的次数。
理论上,线程优先级越高的线程获取CPU时间片的次数越多。
max.setPriority(Thread.MAX_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);

static void sleep(long ms)
线程提供的sleep方法可以让运动起来的线程处于"阻塞状态" ,指定毫秒,
当超时后,线程会自动回到RUNNABLE状态等待再次分配时间片并发运行。
当一个线程处于BLOCK(阻塞)状态时,CPU会立刻释放其时间片,去执行其他线程。

sleep方法要求必须处理 中断异常:InterruptedException
当我们调用一个线程的interrupted方法时,可以中断一个正在运行的线程,但是,若该线程正处于阻塞状态时,那么调用该方法时并不是中断线程,
而仅是中断其阻塞状态,这时会抛出中断异常,通知程序该线程的阻塞状态被中断。
//由于lin线程处于阻塞状态,这边中断的话,lin的sleep方法会抛出中断异常。
Thread lin=new Thread(){...};
lin.interrupt();

获取线程相关信息的一组方法
//获取主线程 
Thread main=Thread.currentThread();
//获取线程名字
main.getName();
//获取唯一标识
long id=main.getId();
//获取线程优先级
int i=main.getPriority();
//是否处于活动状态
boolean i=main.isAlive();
//判断线程是否为守护线程
boolean i=main.isDaemon();
//判断线程是否被中断
boolean i=main.isInterrupted();

-----day09----
"守护线程"
 守护线程又称后台线程。默认创建出来的线程都是前台线程,后台线程是需要在线程启动前单独进行设置的。
设置方法为:
 void setDaemon(boolean on)
  当参数为true时,该线程被设置为守护线程(后台线程)
 
 在使用上, 前台线程与后台线程没有什么区别,但是在结束时机上有一个不同,
 即,当进程结束时,所有还在运行的守护线程都会被强制中断。
  进程的结束:当一个进程中的所有前台线程都结束时,进程结束。

jack.setDaemon(true);//设置为守护线程
jack.start();

多个线程之间是异步运行代码的(各自代码运行之间不存在先后关系,各干各的)
同步运行:执行有先后顺序,通常单线程运行代码是同步的
异步运行:各干各的,多线程执行就是异步执行
 
 线程提供了:
 void join()
"等待该线程终止"
 该方法允许一个线程在join方法所属线程后面等待,直到该线程执行完毕后再继续运行。
所以使用join可以协调线程之间同步运行代码。

注意:                
JDK1.8之前有一个要求(源自JVM内存分配问题):
当一个方法的局部内部类当中想引用该方法的其他局部变量时,该变量必须时final的。
这里main方法的局部内部类show当中想引用download,
而download本身是main方法的一个局部变量,那么该变量就必须是final的
=...

 多线程并发安全问题
 当多个线程并发操作同一资源时,由于线程切换实际不确定,导致程序未按照设计要求的顺序执行而出现了逻辑混乱。严重时可能导致系统瘫痪。
 
 解决多线程并发安全问题的方式就是让多个线程排队访问该资源。将异步操作变为同步操作。

Thread.yield();//模拟线程发生切换
导致当前执行线程处于让步状态 。 如果有其他的可运行线程具有至少与此线程同样高
的优先级, 那么这些线程接下来会被调度 。 注意, 这是一个静态方法 。

异步操作:多线程并发的操作,相当于各干各的。
同步操作:有先后顺序的操作,相当于你干完后我再干。

"锁对象"

当一个方法被synchronized修饰后,那么该方法称为同步方法,
即:多个线程不能同时进入方法内部执行,
只能一个线程执行完,其他线程再进来执行。
将异步操作改为同步操作就解决了并发安全问题。
成员方法上使用synchronized,那么"同步监视器对象"就是当前方法所属对象,
即方法中看到的this

注意:在使用并发时,将域设置为private是非常重要的,否则,synchronized关键字就不能防止其他任务直接访问域,这样就会产生冲突。
"lock???"警告 : 把解锁操作括在 finally 子句之内是至关重要的 。 如果在临界区的代码抛出异常,
锁必须被释放 。 否则 , 其他线程将永远阻塞 。

有效的缩小同步范围可以在保证并发安全的前提下提高并发的效率。
同步块可以更准确的控制需要同步运行的代码片段,语法:
  synchronized(同步监视器对象){
     需要同步运行的代码片段
  }
.....
* 使用同步块精确的控制需要同步的代码片段
* 需要注意,若想让多个线程同步运行控制的代码片段
* 必须保证同步监视器对象多个线程看到的是同一个。
synchronized(this){
    System.out.println(t.getName()+":正在试衣服");
    Thread.sleep(2000);
}

 * 静态方法若使用synchronized修饰后,那么该方法一定具有同步效果。
 * 实际上,静态方法的同步监视器对象为当前类的类对象。
 * 类对象后面在学习反射知识时介绍。
每个类都有唯一的一个类对象。获取类对象的方式:类名.class

//
synchronized /'sɪŋkrənaɪzd/
是Java内建的同步机制,所以也有人称其为Intrinsic Locking,它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他试图获取的线程只能等待或者阻塞在那里。
在代码中,synchronized可以用来修饰方法,也可以使用在特定的代码块上,本质上synchronized方法等同于把方法全部语句用synchronized块包起来。

---day10-------
集合有两个基本接口 : Collection 和 Map 。

集合是一组数据结构,与数组一样,用来保存一组元素。
集合提供了一系列操作元素的相关方法,使用更方便。

"Collection"有两个常用的派生接口:
java.util.List:可重复集且有序
|--List:有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素是可以重复的。
    List提供了增删改查动作   
    增加add(element) add(index,element)  ;
    删除remove(element) remove(index);
    修改set(index,element);
    查询get(index);
java.util.Set:不可重复集-"set(集)"
"元素是否重复是依靠元素自身的equals比较的结果。"
|--Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有一种:迭代器。

boolean add(E e)
向当前集合中添加给定元素,当该元素成功添加则返回true。
由于Set集合是不可重复集,所以添加重复元素是会返回false的。List集合不存在该情况。
int size()
返回当前集合的元素个数
boolean isEmpty()
判断当前集合是否为空集(不含有任何元素)
c.clear();//清空集合

集合提供了判断是否包含给定元素的方法
boolean contains(E e)
若集合包含当前元素则返回true,否则返回false

contains方法会用参数对象与集合现有的元素顺序进行"equals比较"(元素自身的equals方法),若返回值为true,则集合认为包含该元素。
所以元素自身equals方法直接决定集合判断包含该元素的结果。

删除集合元素
boolean remove(E e)
将给定的元素从集合中删除,需要注意,该方法依然是依靠元素"equals比较"。

集合存放的是元素的引用

集合操作
boolean addAll(Collection c)
将给定集合中的所有元素添加到集合中。
添加后当前集合元素发生改变则返回true

boolean removeAll(Collection c)
删除当前集合中与给定集合的共有元素(删除交集部分)

 "遍历集合"--迭代器
 对于Collection这个层面而言,下面的集合实现类不都是有序的,
 所以无法通过如数组那样操作下标来遍历元素。
 Collection提供了统一的遍历集合元素的方式:迭代器模式 
  
 Iterator iterator()
iterator 方法用于返回一个实现了 Iterator 接口的对象 。 可以使用这个迭代器对象依次访
问集合中的元素
 
  java.util.Iterator
 迭代器接口,规定了迭代器遍历集合的相关操作方法,不同的集合都实现了一个用于遍历自身元素的
  迭代器实现类。我们无需记住这些实现类的名字,用多态的角度当它们是Iterator看待即可。
  迭代器遍历集合的规则为:问,取,删
  其中删除元素不是必须操作。

"迭代器"常用方法:
boolean hasNext()//判断当前集合是否还有元素可以迭代
E next()//取出下一个元素
        
迭代器要求在遍历的过程中不要通过集合的方法增删元素,
否则会抛出异常。
//错误 c1.remove(str);
迭代器的remove()方法删除的是上次調用next方法取出的元素。
如果想要删除指定位置上的元素, 仍然需要越过这个元素(it.next()) 。
next 方法和 remove 方法的调用具有互相依赖性 。

::Java的Iterator只能单向移动。
查找操作与位置变更是紧密相连的 。 查找一个元素的唯一方法是调用 next , 而在执行查找操作的同时, 迭代器的位置随之向前移动 。
/*应该将 Java 迭代器认为是位于两个元素之间 。 当调用 next时 , 迭代器就越过下一个元素 , 并返回刚刚越过的那个元素的引用*/
你在迭代一个ArrayList,迭代器的工作方式是一次返回给你第0个元素、第1个元素等,假设迭代到第5个元素的时候,你突然在ArrayList的头部插入了一个元素,使得所有的元素都往后移动,于是你当前访问的第5个元素就会被重复访问。
Java认为在迭代过程中,容器应当保持不变。因此,Java容器通常保留了一个域称为modCount,每次你对容器修改,这个值就加一。当你调用iterator方法时,返回的迭代器会记住当前的modCount ,随后迭代过程中会检查这个值,一旦发现这个值发生变化,就说明你对容器做了修改,就会抛出异常

jdk1.5之后,推出了一个新的特性:增强for循环
也称为:新循环 ,"for each"循环
不取代传统for循环的工作,新循环只用来遍历集合或数组使用。

新循环并非新的语法,编译器认可而不是JVM认可。
编译器在编译源程序时若发现使用新循环遍历数组时,会将代码改为使用普通for循环遍历。

使用新循环遍历集合
"新循环遍历集合,会被编译器改为使用迭代器遍历集合"。
所以在使用新循环遍历集合过程中 ,不要通过集合的方法增删元素。
否则,新循环遍历集合会抛出异常。

---day 11----

 "泛型"
Collection 与 Iterator 都是泛型接口 , 可以编写操作任何集合类型的实用方法
 1,因为集合可以存储的对象类型是任意的,在取出进行向下转型时,容易发生ClassCastException。所以JDK1.5以后就有了解决这个问题的技术:泛型。泛型也称为参数化类型
 意图在于将一个类中属性,方法参数,返回值等类型的定义权交给了使用者,
 这样大大提高了代码的灵活性。
 2,泛型的原理:其实就是在操作的元素类型不确定时,通过传递参数的形式来明确类型。
 3,泛型的体现就是 <参数类型变量>用于接收具体的实际元素类型。
 
 泛型的原型是Object,即:在使用时若不明确指定泛型的实际类型时,则默认按照原型Object使用

使用时,要指定泛型的实际类型//P<String> s1=...
编译器检测实参类型是否符合泛型要求//p1.setX(33);
获取泛型值时,编译器会在编译后加上造型(强转)。
下面的代码会被编译器改为:int x1=(Integer)p1.getX();
实际上还会触发自动拆装箱特性,并最终改改变为)
int x1=((Integer)p1.getx()).intValue();

泛型是编译器认可,而非虚拟机,"面试"常见问题:
/*
Point<Integer> p1=new Point<>(1,2);
p1.setX(2);//编译时检测是否符合泛型要求
int x1=p1.getX();//编译后会添加造型(强转)
Point p2=p1;
p2.setX("三");
x1=p1.getX();//抛出类造型异常
System.out.println("x1:"+x1);//执行不到
*/

泛型在集合中使用广泛,用于约束集合中的元素类型。
迭代器也支持泛型,实际类型与其遍历的集合的泛型一致即可。

java.util.List
List集合:可重复集,并且有序,特点是可以通过下标操作元素
常用实现类:
java.util.ArrayList:内部由数组实现,长度是可变的(原理是创建新数组+复制数组),查询速度很快,增删较慢,不同步的。
java.util.LinkedList:内部由链表实现,不同步的,增删元素性能更好,
尤其是首尾增删元素。查询速度较慢
可用于实现堆栈,队列。

void add(int index,E e)
将给定的元素添加到指定位置
E remove(int index)
 删除指定位置上的元素,并将其返回

List提供了一对get,set方法
    E get(int index)
    返回给定下标处对应的元素
    String str=list.get[i];
    类比: String str=array[i];
    E set(int index, E e)
     将给定元素设置到指定位置,返回值为原位置对应的元素("替换元素"操作)

List提供了获取子集的方法:
List subList(int start,int end)
获取当前集合的"子范围视图"
    对子集操作就是对原集合对应元素的操作。
    "删除list集合"2~8
    list.subList(2, 9).clear();

集合转换为数组
Collections提供了方法toArray,可以将当前集合转换为一个数组
该toArray方法要求传入一个数组,若该数组可用(长度可以存放集合所有元素)则使用该数组,若不可用则会自行创建一个与集合size一样的数组。

数组转换为集合
Arrays是数组的工具类,其提供了一个静态方法:asList
该方法可以将给定的数组转换为一个List集合。
String[] arr={"one","two"};
List<String> list=Arrays.asList(array);
list.set(1,"2");//对集合元素的操作就是对数组对应元素的操作
//list.add("six");由于数组是定长的,所有该集合不允许增删元素,否则会抛出异常。

 "集合的排序"
java.util.Collections是集合的工具类,提供了一系列操作集合的静态方法。其中sort方法可以对List集合进行自然排序
 即:从小到大排序集合元素
//排序
Collections.sort(list);        
//乱序
Collections.shuffle(list);

排序自定义类型元素的List集合
Collections.sort(list);//sort方法要求集合元素必须实现Comparable接口,否则编译不通过。

this compareTo o
当实现了Comparable接口后,就必须重写抽象方法compareTo,该方法的作用是定义当前对象与参数对象比较大小规则。
该方法的返回值为int值,该值不关心具体取值,关心的是取值范围。
当返回值>0:当前对象大于参数对象  this>o
当返回值<0:当前对象小于参数对象
当返回值=0:两个对象相等           

public int compareTo(Point o) {
    return 0;
}


---day 12---------
元素的排序比较有两种方式:
1,元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。
如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式。
2,比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。
    需要定义一个类实现Comparator接口,重写compare方法。
通常使用匿名内部类创建比较器

Collections的sort(List list)方法是按照集合元素自身的比较规则比较后由小到大进行排序的。
而有时集合元素已经实现了比较规则,但是该规则不满足我们排序的需求。

当排序含有自定义类型元素的集合时,应当使用重载的需要传入额外"比较器"的sort方法:
static void sort(List list,Comparator com)
该方法会使用该比较器的比较规则对集合元素进行比较并按照比较的结果进行排序
 因为该方法不要求集合元素必须实现Comparator接口,那么该方法对我们集合元素Point就没有侵入性。
 侵入性:当我们需要调用某个功能方法时,该方法要求我们的程序为其修改其他的额外代码,修改的越多,
 侵入性越强。侵入性对后期系统维护不利,在开发中应当尽量避免。

java.util.Queue /kjuː/ 
 "队列"
 队列是经典的数据结构,可以保存一组元素,但是存取元素必须遵守先进先出原则。

LinkedList实现了队列接口,因为链表本身可以保存一组元素,
 并且特点是首尾增删元素效率高,这正好满足队列特点。
 boolean offer(E e) ['ɒfə]//入队操作,
 将给定元素添加到队列末尾。
 E poll() [pəʊl]//出队操作
获取队首元素,获取后该元素即从队列中被删除。
 E peek()//引用队首元素。
 
由于队列接口继承自Collection,所以可以使用迭代器遍历队列,并且不会影响队列中的元素。
用poll方法遍历队列元素
while(queue.size()>0) {
    System.out.println(queue.poll()+":"+queue.size());
}

双端队列 java.util.Deque [dek]
 
Deque接口继承自Queue,双端队列是两端都可以做进出队操作的队列。
并且提供了明确方向的进出队方法。
常见实现类:java.util.LinkedList
  
Queue 方法    等效 Deque 方法
offer(e)    offerLast(e)
poll()    pollFirst()
peek()    peekFirst()
add(e)    addLast(e)
remove()    removeFirst()
element()    getFirst()



栈也是一个经典的数据结构,可以保存一组元素,但是,存取必须遵循"先进后出"原则。
通常使用栈是为了实现“后退”这样的功能。
  
 Deque双端队列可以实现栈操作,并且为栈也提供了对应的入栈与出栈方法(push,pop)
堆栈方法    等效 Deque 方法
push(e)    addFirst(e)
pop()    removeFirst()
peek()    peekFirst()

线程安全的集合
集合常用的实现类有:ArrayList,LinkedList,HashSet
*它们都不是线程安全的,在多线程并发操作时会出现并发安全问题。
* Collections提供了一组静态方法,可以将现有的集合转换为一个线程安全的.

//将给定的list集合转换为一个线程安全的List
list = Collections.synchronizedList(list);
//将给定的set集合转换为线程安全的
set = Collections.synchronizedSet(set);

API手册上也有说明,对于一个线程安全的集合而言,
也不与迭代器遍历该集合的操作做互斥。所以若存在
多个线程遍历 和 增删元素同时操作的情况,要自行
维护遍历与增删元素的互斥

阻塞队列,双缓冲队列
阻塞队列是并发安全的队列,在多线程情况下使用。
并且由于内部由双缓冲实现,并发效率比较好。
BlockingQueue
常用实现类:
ArrayBlockingQueue
LinkedBlockingQueue
LinkedBlockingQueue<String> queue
            = new LinkedBlockingQueue<String>();

阻塞式入队

try {
/*
* 对于有界队列,有可能会存在队列满了存不进去的情
* 况,这时可以使用阻塞入队操作,指定超时时间以及
* 时间单位,在规定时间内若可以将元素入队则方法执
* 行完毕,否则会抛出超时异常。在等待的过程中会发
* 生该线程阻塞现象。
*/
queue.offer("four", 500, TimeUnit.MILLISECONDS);            
            
---day 14------
java.util.Map
Map(映射)查找表?,常用的数据结构,体现的样子为一个多行两列的表格。
常用的实现类:HashMap

java.util.HashMap:"散列表",使用散列算法实现的Map,当今
查询速度最快的数据结构。也称为"哈希表"

Map分为两列,左列称为key,右列称为value
key通常保存的是查询条件,value保存的是对应的值。
Map<String,Integer> map = new HashMap<>();

 V put(K k,V v)
 将给定的一组键值对存入到Map中。 注意,Map有一个要求,key在Map中不允许重复, 是否重复是依靠key元素自身equals比较的结果。若使用重复的key存入新的value,那么该操作为替换value操作,这是put方法返回值为被替换的 原value,若put的key在map中不存在,则直接保存该组键值对并且返回值为null。

 需要注意,若value的类型是一个包装类,那么在接收put方法的返回值时不要用其对应的基本类型接收,避免出现自动拆箱。因为若put方法返回值为null时自动拆箱会引发空指针异常。
Integer value = map.put("语文", 99);

 V get(K k)
 根据给定的key获取对应的value
 若给定的key在Map中不存在,则返回值为null
value = map.get("数学");

 V remove(K k)
 删除给定的key所对应的这组键值对,返回值为该
 key所对应的value。
value = map.remove("语文");

"Map的遍历分三种"
1,遍历所有的key
2,遍历每组键值对
3,遍历所有的value(相对不常用)

"LinkedHashMap"是"有序的Map",遍历时的顺序可以与
put元素时的顺序一致。

遍历所有的key
Set<K> keySet()
将当前Map中所有的key以一个Set集合形式返回
遍历该Set集合等于遍历了所有的key
Set<String> keySet = map.keySet();
for(String key : keySet) {
    System.out.println("key:"+key);
}

 遍历每一组键值对
java.util.Map.Entry
"Entry"用来定义Map中每一组键值对,即:每个实例表示一个具体的键值对。
Entry提供了获取其表示的键值对中key与value的方法
 K getKey()
 V getValue()
 
Map提供了遍历键值对的方法:
Set<Entry> entrySet()
 将当前Map中每组键值对(Entry实例)存入Set集合并返回
Set<Entry<String,Integer>> entrySet 
                                = map.entrySet();
for(Entry<String,Integer> e : entrySet) {
    String key = e.getKey();
    Integer value = e.getValue();
    System.out.println(key+":"+value);
}

遍历所有的value
Collection values()
将所有的value存入一个集合后返回

Collection<Integer> values = map.values();
  for(Integer value : values) {
    System.out.println("value:"+value);
}

判断当前Map是否包含给定的key或value
 boolean containsKey(K k)
 boolean containsValue(V v)

HashMap并非线程安全的,同样可以使用Collections将一个Map转换为线程安全的
map=Collections.synchronizedMap(map);

"HashMap"
HashMap之所以是查询速度最快的数据结构,是因为HashMap本身也是由数组实现,但是它根据key的hashcode值计算出其应在数组的位置并将其存放,这样当根据key查找时也直接根据其
hashcode值计算下标快速定位到数组对应位置检索对应的value
 这样省去了遍历数组的操作,从而不受数据大小影响查询性能。
 
 对于Key而言,其hashcode方法与equals方法直接影响其在HashMap内部数组的位置,所以要妥善重写这两个方法,避免在数组中产生链表,否则会影响散列表查询性能。
 
 产生链表的原因之一:当两个Key的hashcode值相同时,计算对应的数组下标位置相同,但是这两个Key的equals不同(不是同一个key,若equals相同Map会认为是相同的key,那么就做替value操作了)这时就会在数组该位置处产生链表。
 
java提供的类都妥善的重写了equals与hashcode,包括Object类都满足该重写规则,但是若我们自定义的类若需要重写equals方法时应当一同重写hashcode,这在API手册上有明确说明。
 
  重写规则:
  1:成对重写,即:当我们重写一个类的equals方法时就应当连同重写hashcode方法。
 2:一致性,即:当两个对象的equals比较为true时,hashcode返回的数字必须相等。反之虽然不是必须的,但是最好也要保证当两个对象hashcode相等时,equals比较也为true。否则会在  HashMap中作为key使用时产生链表,影响查询性能。
 3:稳定性,即:当参与equals比较的属性值没有发生改变的前提下,
多次调用hashcode方法返回的数字不能改变。  


Collection:
        |--List:有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素是可以重复的。
                List提供了增删改查动作   
                增加add(element) add(index,element)  ;
                删除remove(element) remove(index);
                修改set(index,element);
                查询get(index);
            |--Vector:可以增长的数组结构。同步的。效率非常低。已被ArrayList替代。
            |--ArrayList:是数组结构,长度是可变的(原理是创建新数组+复制数组),查询速度很快,增删较慢,不同步的。
            |--LinkedList:是链表结构,不同步的,增删速度很快,查询速度较慢。
                    可用于实现堆栈,队列。
                    堆栈:先进后出  First in Last Out  FILO 手枪弹夹。
                    队列:先进先出  First in First Out FIFO 排队买票。
            List可以存储重复元素的,如果需求中要求容器中的元素必须保证唯一性。
            
        |--Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有一种:迭代器。
            |--HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法。查询速度快。
            |--TreeSet:可以对Set集合中的元素进行排序。使用的是二叉树结构。如何保证元素唯一性的呢?
                        使用的对象比较方法的结果是否为0,是0,视为相同元素不存。
                        元素的排序比较有两种方式:
                        1,元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。
                        如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式。
                        2,比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。
                            需要定义一个类实现Comparator接口,重写compare方法。
            到此为止:再往集合中存储对象时,通常该对象都需要覆盖hashCode,equals,
            同时实现Comparale接口,建立对象的自然排序。通常还有一个方法也会复写toString();
            
    看集合对象的小技巧:★★★★★★
    集合分体系。List  Set
    子类对象的后缀名是所属体系,前缀名是数据结构名称。
    List:新出的子类都是以List结尾的,通常都是非同步的。
        |--ArrayList :看到array,就知道数组,查询速度快。
        |--LinkedList:看到link,就知道链表,增删速度快。
        
    Set:
        |--HashSet:看到hash,就知道哈希表,查询速度更快,并想到元素唯一,通过hashCode(),equals方法保证唯一性。
        |--TreeSet:看到tree,就知道二叉树,可以排序,排序想到Comparable-compareTo Comparator--compare方法。


---day 15---
"XML"

XML 是一种标记化语言,其中所有的东西都要被正确的标记,以产生形式良好的文档。
XML 用来描述数据,而 HTML 则用来显示数据。
解析XML文档

使用DOM解析XML的大致步骤
1:创建SAXReader
2:使用SAXReader读取指定的xml文档并生成Document对象
这一步就是DOM解析耗时耗资源的地方,因为要先将XML文档全部读取完毕并以Document对象形式保存在内存内。
3:通过Document获取根元素
4:根据XML文档的结构逐级获取子元素以达到遍历XML文档的目的。
org.dom4j.io.SAXReader

SAXReader reader=new SAXReader();
Document doc=reader.read(new FileInputStream("emp.xml"));

"方法"
Document提供了获取根元素的方法
Element getRootElement()
Element的每一个实例用于表示XML文档中的一个元素(一对标签)
Element提供了获取其表示的元素的相关方法常用的有:

String getName()//获取当前标签的名字
String getText();//获取当前标签中间的文本,例如<a>获取的文本</a>
String getTextTrim();//

Element element(String name)//获取当前标签中指定的子标签
List elements();//获取当前标签中所有的子标签
List elements(String name)//获取当前标签中所有同名(指定的名字)子标签

ele.element("age").getText();等价于
ele.elementText("age")

获取<emp id="x">标签的属性id对应的值
Element提供了获取属性的方法:
Attribute attribute(String name)
获取指定名字的属性
 
Attribute的每个实例用于表示某个标签中的一个属性
 其有两个常用方法:
 String getName():获取属性的名字
 String getValue():获取属性的值
 
 Element也提供了快捷获取属性的方法:
 String attributeValue(String name)
 获取当前标签中指定名字对应的属性的值。

===============
=================
===================
"反射"
"Java反射机制"
反射机制允许我们的程序在运行时动态加载一个类,并实例化,或者获取其定义的相关信息(属性、方法等),甚至可以动态调用这些方法。而非传统的在编码期间以编码形式实例化或调用方法等。
这样做大大的提高了代码的灵活度。但是反射也是一把双刃剑,过度使用会降低系统性能。

"Java核心技术"
能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,反射可以用来:
1,在运行时分析类的能力
2,在运行时查看对象,例如:编写一个toString方法供所有类使用。
3,实现通用的数组操作代码
4,利用Method对象,这个对象很像C++中的函数指针。


Class类
Class 类实际上是一个泛型类 。

Class类的每一个实例是用来表示一个被JVM加载的类。
通过它可以了解到其表示的类的相关信息,比如:有哪些属性,构造方法,方法等等。
所以我们也称Class的实例叫作类的类对象

在JVM内部,每个被加载的类都有且只有唯一的一个Class的实例表示它。
那么这个Class的实例是在JVM加载一个类的时候创建的。
想获取一个类的类对象可以通过下面几种方式得到:
1:每个类都有一个静态属性class,可以得到这个类的类对象
例如:
    Class cls=Person.class;
    通过这个cls可以获取所有有关Person的信息。
     比如,Person有哪些方法,哪些属性,构造方法等信息。
2:通过Class的静态方法forName()加载指定的类并得到其类对象。
例如:
    Class cls=Class.forName("object.Person");
    加载object包下的Person类,需要注意,这里的参数必须写类的完全限定名(包名.类名)
3:还可以通过类加载器加载一个类

获得Class类对象的三种方法
Class cl1=Double[].class;
Class cl2=String.class;
Class cl3=int.class;
注意:一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如:int不是类,但int.class是一个Class类型的对象

"利用反射机制"实例化一个类

//1,加载Person的 类对象
Class cls=Class.forName("cn.Person");
// 2,通过类对象实例化其表示的类的实例,Class提供了一个newInstance()方法,该方法 调用其表示的类的无参构造方法实例化它。
Object o=cls.newInstance();

newInstance()可以用来动态的创建一个类的实例
将forName与newInstance配合使用,可以根据存储在字符串中的类名创建一个对象
注意:如果需要以这种方式向希望按名称创建的类的构造器提供参数, 就不要使用上面那条语句 , 而必须使用 Constructor 类中的 newlnstance 方法 。

使用反射"调用方法"
 1,加载对应的类
 2,获取该类定义的要调用的方法
 3,实例化该类
 4,调用该方法
Class cls=Class.forName("reflect.Person");
Method method=cls.getDeclaredMethod("sayHi",null);//方法名、参数数组
Object o=cls.newInstance();
method.invoke(o, null);

2
Method的每一个实例用于表示一个类中定义的一个方法
 通过Method可以获取与表示的方法相关的信息,比如:
 方法的返回值类型,方法名,参数,访问修饰符等等
 甚至可以调用该方法。
* 下面的例子:
* method实例表示的是Person类的成员方法sayHello()

"调用有参方法"
 获取有参方法
getDeclaredMethod第二个参数是一个Class类型的数组。
数组的每个元素用于指定该方法的参数类型。
数组元素的顺序必须与获取的方法的参数顺序一致
例如:
获取 sayName(String name,int age)
Method method=cls.getDeclaredMethod("sayName",new Class[] {String.class,int.class});
Object o=cls.newInstance();
/*invoke方法在调用方法时,第二个参数为Object类型数组,
 数组每个元素为调用该方法时传入的一个实际参数*/
method.invoke(o, new Object[]{"张三",34});

"反射调用私有方法"
//私有方法在设置强制方法后,是可以执行的
method.setAccessible(true);//强制访问,破坏了封装特性

在 java.lang.reflect包中有三个类 Field 、Method 和 Constructor分别用于描述类的域 、方法和构造器 。这三个类都有一个叫做 getName 的方法, 用来返回项目的名称 。 Field 类有一个 getType 方法, 用来返回描述域所属类型的 Class 对象 。 Method 和 Constructor 类有能够报告参数类型的方法 , Method 类还有一个可以报告返回类型的方法 。 这三个类还有一个叫做 getModifiers 的方法 , 它将返回一个整型数值, 用不同的位开关描述 public 和 static 这样的修饰符使用状况 。 另外, 还可以利用 java.lang.reflect 包中的 Modifiei 类的静态方法分析getModifiers 返回的整型数值 。 例如, 可以使用 Modifier 类中的 isPublic 、isPrivate 或 isFinal判断方法或构造器是否是 public 、 private 或 final 。 我们需要做的全部工作就是调用Modifier类的相应方法, 并对返回的整型数值进行分析, 另外 , 还可以利用 Modifier. toString 方法将修饰符打印出来 。
Class 类中的 getFields 、 getMethods 和 getConstructors 方 法 将 分 别 返 回 类 提 供 的public域 、方法和构造器数组, 其中包括超类的公有成员 。 Class 类的 getDeclareFields 、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域 、 方法和构造器, 其中包括私有和受保护成员, 但不包括超类的成员 。


"加餐"
"线程池"
线程池主要有两个作用:
1,控制线程数量
2,重用线程
线程数量过多会导致内存消耗大,并且会产生CPU过度导致整体并发性能降低的问题。
并且频繁的创建销毁线程也会给系统带来负担。
为此,在实际开发中出现上述情况时我们都应当使用线程池来管理线程。
//创建固定大小的线程池(线程数量为2)
ExecutorService threadPool=Executors.newFixedThreadPool(2);
// 2  2  1  什么意思???
for(int i=0;i<5;i++) {
    Runnable runn=new Runnable() {
        public void run() {
            Thread t=Thread.currentThread();
            try {
                System.out.println(t.getName()+"正在运行任务...");
                Thread.sleep(4000);
                System.out.println(":运行任务完毕");
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    };
    //将任务交给线程池
    threadPool.execute(runn);
    System.out.println("指派了一个任务给线程池");
}
/*
 * 停止线程池
 * shutdown():调用后,线程池不会马上停止,这时的线程池不再接受新的任务,
 * 并且会将所有已经在线程池中的任务全部执行完毕后停止。
 * shutdownNow():线程池会强制中断所有正在运行的线程并立即停止。
 */
//停止线程池
threadPool.shutdown();
System.out.println("线程池关闭了!");


-----day17----

"Date"
"SimpleDateFormat"
"Calendar"
Java.util.Date
Date的每一个实例用于表示一个具体的时间。
由于Date存在时区以及千年虫问题,所以大部分方法在1.1版本就被声明为过时的。
 现在使用Date通常仅用于表示时间。

long getTime()
该方法返回一个long值,该值就是当前Date内部维护的long值,表示自1970年1月一日0点0时0秒到当前Date表示的时间之间所经过的毫秒
"1 天 = 24 × 60 × 60 = 86400 秒"
也可以设置一个long值,让Date表示该时间
data.setTime(time);
或,new Date(time);

java.text.SimpleDateFormat
SDF是用来在"String与Date之间相互转换"的API。
SDF转换是基于一个给定的日期格式。
//MM代表“月” mm代表“分钟” HH代表小时(24制),hh为12制
String pattern="yyyy-MM-dd HH:mm:ss E ";
SimpleDateFormat sdf=new SimpleDateFormat(pattern);
 String format(Date date)
该方法会将给定的Date按照当前SDF指定的日期格式
转换为对应的字符串" Date->String"
String line=sdf.format(date)

将一个字符串解析为Date
//String str="2098-09-23 10:41:29";
//Date date=sdf.parse(str);
Date parse(String str)
将给定的字符串按照当前SDF指定的日期格式解析为Date对象

"java.util.Calendar"
日历类,使用日历类可以对时间进行一系列对操作。
Date大部分操作时间的方法都被声明为过时的,取而代之的也是使用Calendar的相关操作。

Calendar是一个抽象类,定义了日历类操作时间的功能,常用实现类为:
java.util.GregorianCalendar,即:阳历

Calendar提供了一个静态方法:getInstance,该方法可以获取一个当前系统所在地区适用的实现类,
大部分地区返回的也是阳历实现类。
Calendar cal = Calendar.getInstance();

Calendar默认创建也表示当前系统的时间。
但是toString返回的字符串不能直观的体现。

System.out.println(cal);

Calendar提供了与Date之间互转的方法
Date getTime()
将当前Calendar表示的日期以Date实例形式返回。

* void setTime(Date date)
* 使用当前Calendar表示给定的Date所表示的日期
*/
Date date=cal.getTime();//Calendar转Date
System.out.println(date);
cal.setTime(date);//Date转Calendar

=============
"Lamdba"表达式
JDK8推出的一个特性,可用来快速便捷的创建匿名内部类
 
语法:
([arg1,arg2...])->{
     方法体
 }
lambda表达式在创建一个匿名内部类时有一个要求, 就是其对应接口必须"有且只能有一个抽象方法"时才能使用, 如果存在多个方法时使用,编译器是不通过的。
/*
Runnable r=()->{syso...};
//若重写的方法只有一句代码,那么该方法的“{ }”可以省略
Runnable r2=()->syso...;
*/

"方法含有参数"
重写方法若含有参数,参数类型可以忽略。
编译器会结合上下文判定参数类型。
/*
FileFilter filter=(f)->{
    return f.getName().endsWith(".txt");
};*/
若可以忽略方法的"{ }",那么当这个方法要求返回值时,return关键字必须省略。
//FileFilter fileter=(f)->f.getName().endsWith(".txt");

JDK1.8之后,集合和Map推出了为lambda表达式实现的遍历操作。
并且对于一个线程安全的集合或Map而言,使用这种遍历方式时可以保证并发安全的。
而以前使用迭代器则不能保证,需要自行维护并发安全。
/*
for(String str:list){
    System.out.println(str);
}
lambda表达式实现的遍历操作
list.forEach((str)->System.out.println(str));
*/
Map<String,Integer> map=new HashMap<>();
map.put("语文", 99);
map.put("数学", 94);
map.put("韩文", 92);

map.forEach((k,v)->System.out.println(k+":"+v)); 


处理器 ( handler )

Collection:
        |--List:有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素是可以重复的。
                List提供了增删改查动作   
                增加add(element) add(index,element)  ;
                删除remove(element) remove(index);
                修改set(index,element);
                查询get(index);
            |--Vector:可以增长的数组结构。同步的。效率非常低。已被ArrayList替代。
            |--ArrayList:是数组结构,长度是可变的(原理是创建新数组+复制数组),查询速度很快,增删较慢,不同步的。
            |--LinkedList:是链表结构,不同步的,增删速度很快,查询速度较慢。
                    可用于实现堆栈,队列。
                    堆栈:先进后出  First in Last Out  FILO 手枪弹夹。
                    队列:先进先出  First in First Out FIFO 排队买票。
            List可以存储重复元素的,如果需求中要求容器中的元素必须保证唯一性。
            
        |--Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有一种:迭代器。
            |--HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法。查询速度快。
            |--TreeSet:可以对Set集合中的元素进行排序。使用的是二叉树结构。如何保证元素唯一性的呢?
                        使用的对象比较方法的结果是否为0,是0,视为相同元素不存。
                        元素的排序比较有两种方式:
                        1,元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。
                        如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式。
                        2,比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。
                            需要定义一个类实现Comparator接口,重写compare方法。
            到此为止:再往集合中存储对象时,通常该对象都需要覆盖hashCode,equals,
            同时实现Comparale接口,建立对象的自然排序。通常还有一个方法也会复写toString();
            
    看集合对象的小技巧:★★★★★★
    集合分体系。List  Set
    子类对象的后缀名是所属体系,前缀名是数据结构名称。
    List:新出的子类都是以List结尾的,通常都是非同步的。
        |--ArrayList :看到array,就知道数组,查询速度快。
        |--LinkedList:看到link,就知道链表,增删速度快。
        
    Set:
        |--HashSet:看到hash,就知道哈希表,查询速度更快,并想到元素唯一,通过hashCode(),equals方法保证唯一性。
        |--TreeSet:看到tree,就知道二叉树,可以排序,排序想到Comparable-compareTo Comparator--compare方法。

---day10-刘苍松------------
"2进制"
什么是二进制?
逢2进1的计数规则
计算机内部只有2进制

int i=50;
String Integer.toBinaryString(i);
将i在内存中实际的2进制数据作为字符串返回。

"16进制"
/*16进制用于简写2进制*/,16进制可以对2进制进行缩写,从后向前每4位2进制缩写为一位16进制数字。
"补码"
补码:将一个固定位数的2进制数分一半为负数的编码规则。
核心解决问题:解决负数问题!
int的最大值与最小值
max:
01111111 11111111 11111111 11111111
或0x7fffffff;
min:
10000000 00000000 00000000 00000000
或0x8000000;  

互补对称公式
-n = ~n +1

2进制计算
1,~取反计算
2,& 按位与  有0则为0(乘法)
    可以与0xff来"截取"后8位数!
3,| 按位或 有1则为1
    可以"拼接"
4,>>>逻辑右移位计算
    计算规则:将数字整体向右移动,低位自动溢出舍弃,高位补0
5,>>数学右移位计算
    2进制时候,数字向右移动一次数值减少2倍(像10进制小数点向左移动一样)
6,<<左移位计算
    规则:将数字整体向左移动,高位自动溢出舍弃,低位补0

&与
n      01110110 10101001 01000011 11110101 
m      00000000 00000000 00000000 11111111
k=n&m  00000000 00000000 00000000 11110101 
如上计算的意义: 计算结果k是数据n的最后8位数!将n后8位截取到k中,m用于实现截取功能,称为m为mask(面具)。当前的m称为8位mask(掩码)
与&(and)计算经常用在"掩码计算",用于截取一个数据部分。

| 或
n      00000000 00000000 00000000 11001110
m      00000000 00000000 11101111 00000000
k=n|m  00000000 00000000 11101111 11001110
如上计算将 n 与 m 拼接在一起了!

>>> 逻辑右移位,无论正负高位都补0,不符合数学运算规律 
>> 数学右移位, 正数高位补0,负数高位补1,相当于数学除法向小方向取整数的结果。

案例:
n       11111111 11111111 11111111 11101100   -20
m=n>>1  111111111 11111111 11111111 1110110   -10
k=n>>2  1111111111 11111111 11111111 111011   -5
k=n>>3  11111111111 11111111 11111111 11101   -3

新建就绪阻塞死亡


代码复用 与 代码重构的区别!!!!!!!!!!!!!
一、什么是重构 
         重构就是通过调整程序代码,但并不改变程序的功能特征,达到改善软件的质量、性能,使程序的设计模式和架构更趋合理,更容易被理解,提高软件的扩展性和维护性。 
二、为什么要代码重构 
          需求的不断变更是重构的最根本原因,而且重构是每一个开发人员都要面对的功课。 
          代码架构最初的设计也是经过精心的设计,具有良好架构的。但是随着时间的推移、需求的剧增,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。bug越来越多,越来越难维护,新的需求越来越难实现,最初的代码构架对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就使这个app的生命走到了尽头。 
代码重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后,使用重构的方式,不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的结构,使系统对于需求的变更始终具有较强的适应能力。 
JDK1.8??? equals..
if (getClass() != obj.getClass())
            return false;

System.in键盘输入
System.out输出到控制台

acg和谐社区壁纸??ecg?
异形壁纸
:)

如何提取java代码中的文档:借助jdk/bin/javadoc.exe指令

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值