Java Note
Java Note
1. 关于包装类----------------------------------------------------------------1
2. 关于异常类----------------------------------------------------------------1
3. 关于 String 方法----------------------------------------------------------2
4. 关于 Random 方法---------------------------------------------------------3
5. 关于迭代器 Interator-------------------------------------------------3
6. ArrayList 与 Vector 的源码要点----------------------------------3
7. 关于 Map 中的键值对(K,V)问题-------------------------------------4
8. Collections 中 copy 方法的注意事项----------------------------5
9. HashSet 和 TreeSet 注意事项-----------------------------------------5
10. 泛型注意事项-------------------------------------------------------------6
11. 事件处理机制关系图-----------------------------------------------------6
12. 线程相关问题--------------------------------------------------------------6
13. 抽象类与接口的区别----------------------------------------------------10
14. 关于 IO 的注意事项----------------------------------------------------10
15. 关于 TCP 的注意事项--------------------------------------------------12
16. 关于 TCP 的相关指令---------------------------------------------------14
17. 关于 UDP 的注意事项-------------------------------------------------15
18. 关于反射的要点提及--------------------------------------------------16
19. 关于 JDBC 的要点提及------------------------------------------------19
20. 关于正则表达式的要点提及----------------------------------------22
21. 关于数据库连接池原理-----------------------------------------------24
22. 关于父类与子类的向上转型的注意事项------------------------24
作者:蒋智康
1
Note
1. 关于包装类>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
(1)Integer
Integer in1=new Integer(1);
Integer in2=new Integer(1);
[Link](in1==in2);
像这样,输出的结果为 false,应为这里 new 了两个包装类,所以属于不同的地址
Integer in1=128;
Integer in2=128;
[Link](in1==in2);
像这样,输出的结果为 false,这里的值为 128,由于其值范围是-128~127
由于底层方法: public static Integer valueOf(int i) {
if (i >= [Link] && i <= [Link])
return [Link][i + (-[Link])];
return new Integer(i);
这里直接跳过了 if 语句,进入了第二个 return,所以又进行了 new 地址,从而由于地址不对而报 false.
(2) String
Person p1=new Person();
[Link]="aa";
Person p2=new Person();
[Link]="aa";
//p1 和 p2 都指向的堆
[Link]([Link]([Link]));//true 比较的是内容
[Link]([Link]==[Link]);//true 最终都指向同一个常量池
[Link]([Link]=="aa");//true 这里"aa"指向的是常量池
1
String str="1234a";
int num2=[Link](str);
[Link]("result="+num2);
} catch (NumberFormatException e) {
//[Link]();
[Link]("异常信息:"+[Link]());
}finally {
[Link]("代码运行完毕!");
}
}
//finally,不管运行时包不报错,最终都会运行到 finally 中
3. 关于 String 方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
(1) StringBuffer 类
StringBuffer 与 String 的区别:
String:保存的是字符串常量,里面的值不能更改,更新即更新地址,效率较低,复用率高
StringBuffer: 保存的是字符串常量,里面的值可以更改,更新即更新内容,效率较高
例:String s=”a”; //创建了一个字符串
s+=”b”; //实际上原来的”a” 字符串对象已经丢弃了,现在又产生了一个字符串,也就是 s+”b”=>”ab” 如果多次进行这些
改变串内容的操作,会导致大量副本字符串对象存留在内存中。
StringBuilder、StringBuffer 与 String 的选择:
1.如果字符串存在大量的修改操作,一般使用 StringBuffer 或 StringBuilder
2.如果字符串存在大量的修改操作,并在单线程时,使用 StringBuffer
3.如果字符串存在大量的修改操作,并在多线程时,使用 StringBuilder(线程不安全)
4.如果字符串存在很少的修改操作,被多个对象的引用,使用 String,比如配置信息等
串行化:对象的寿命通常随着生成该对象的程序的终止而终止。
对象通过写出描述自己状态的数值来记录自己,这个过程叫串行化.
int lastIndexOf(String/int ch) 返回指定字符在此字符串中最后一次出现处的索引。
int indexOf(String/int str) 返回指定子字符串在此字符串中第一次出现处的索引。
StringBuffer insert(int offset, char c) 将 char 参数的字符串表示形式插入此序列中。
例 1: 给每三个数字后面赋一个逗号分隔
String sale="123456789.98";
StringBuffer sb = new StringBuffer(sale); //使其串行化
2
使用指定的语言环境、格式字符串和参数返回一个格式化字符串。
public static void setname(String name) {
String[] res = [Link](" "); //进行分隔
if ([Link] != 3) {
[Link]("格式错误");
return;
}
//进行格式化
name=[Link]("%s,%s.
%c",res[2],res[0],res[1].toUpperCase().charAt(0);
[Link](name);
}
4. 关于 Random 方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
若要获取一个 a~b 之间的一个随机数,则:
int num=(int)([Link]()*(b-a+1)+a);
5. 关于迭代器 Interator>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
boolean hasNext() 如果仍有元素可以迭代,则返回 true。
E next() 返回迭代的下一个元素。
Collection co = new ArrayList(); //向上转型
[Link](new Person("张三"));
[Link](new Person("李四"));
Iterator it = [Link](); //这里启用迭代器
while ([Link]()) {
next 部分源码(用 Object 类型原因):
Object[]elementData=[Link];
Object obj = [Link]();
[Link]("result=" + obj);
}
如果还想进行从头遍历的话,应该重置启用迭代器,因为之前迭代器的指针指向最后一个
重启迭代器:it = [Link](); 然后再进行遍历即可
3
elementDate 为 10;如需要再次扩容,则扩容 elementDate 为 1.5 倍
(2) 如果使用的是指定大小的构造器,则初始 elementDate 容量为指定大小,如果需要扩容,则直接扩容 elementDate
为 1.5 倍.
[Link] 源码:
int oldCapacity = [Link];
int newCapacity = [Link](oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
capacityIncrement > 0 ? capacityIncrement : oldCapacity//这里进
行扩容
/* preferred growth */);
return elementData = [Link](elementData,
newCapacity);
(1) 当创建 Vector 对象时,如果使用的是无参构造器,默认 elementDate 容量为 10,
满后,按 2 被扩容;如果指定大小,则每次直接按 2 倍扩容.
7. 关于 Map 中的键值对(K,V)问题>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
第一种遍历:
Set key=[Link]();
for(Object k:key) {
Employee res=(Employee)[Link](k);
if([Link]()>18000)
[Link](k+"-"+[Link](k));}
4
第二种遍历:
Set<[Link]<K,V>> entrySet()返回此映射中包含的映射关系的 Set 视图。
public static interface [Link]<K,V>映射项(键-值对)。
Set entry=[Link](); //获取 K-V
Iterator it = [Link]();
while([Link]()) {
[Link] en=([Link])[Link](); //转换为 [Link] 型
Employee emp=(Employee)[Link](); //调用 Entry 类中的 getValue 方法
if([Link]()>18000) {
[Link](emp);
}
}
8. Collections 中 copy 方法的注意事项>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ArrayList arr = new ArrayList();
[Link]("a");
[Link]("l");
ArrayList arr1=new ArrayList();
[Link](arr1, arr); //这样的话会直接抛异常
源码:
//这里是先进行大小的比较,若原来的长度大于目标长度,则抛出越界异常
public static <T> void copy(List<? super T> dest, List<? extends T>
src) {
int srcSize = [Link]();
if (srcSize > [Link]())
throw new IndexOutOfBoundsException("Source does not
fit in dest");
解决方法: 将原来目标复制地点 arr1 初始化内存
9. 关于 HashSet 与 TreeSet>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
HashSet 与 TreeSet 分别的如何去重的:
(1) HashSet 去重机制:调用源码中的 hashCode()+equals()快捷键,对哈希值进行运算,得出一个索引值
即为要存放在哈希表中的位置号,如果该位置上没有其它元素,则直接存放,若有,则进行 equals 判断,不相同则加入,相同则不加入.
(2) TreeSet 去重机制:如果你传入了一个 Comparator 匿名对象,就使用 compare 去重,如果方法返回 0,
则认为相同,不添加;如果你没有传入一个 Comparator 匿名对象,则以你添加的 Comparable 接口的 compateTo 方
法进行去重。
TreeSet:
TreeSet tree=new TreeSet();
[Link](new per());
class per{}
以上代码会报异常,因为源码中继承了 Comparator 接口,但是未实现 Comparator 接口
源码:
Comparable<? super K> k = (Comparable<? super K>) key;
即把 per 转成 Comparator 类型
这时,就会报一个 [Link] 类型转换异常
5
解决方法:
在 per 类中,实现 Comparable 接口
class per implements Comparable{
@Override
public int compareTo(Object o) { //这里根据实际情况改写,默认是只返回一个值
return 0;
}}
10. 泛型注意事项>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
已知:CC 继承了 BB,BB 继承了 AA
ArrayList<Object> List1 = new ArrayList<>();
ArrayList<String> List2 = new ArrayList<>();
ArrayList<AA> List3 = new ArrayList<>();
ArrayList<BB> List4 = new ArrayList<>();
ArrayList<CC> List5 = new ArrayList<>();
如果调用 printCollection1 方法,则只有 List3,List4,List5 正确
如果调用 printCollection2 方法,则只有 List1,List3 正确(其中 List2 与 List3 没有关系)
// ? extends AA 表示 上限,可以接收 AA 类或者 AA 类的子类
public static void printCollection1(List<? extends AA> node) {
for (Object res : node) {
[Link](res);
}
}
// ? super 子类类名 AA:支持 AA 类以及 AA 类的父类,不限于直接父类
public static void printCollection2(List<? super AA> node) {
for (Object res : node) {
[Link](res);
}
}
11. 事件处理机制关系图>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
用户
事件
事件源
操作 事件监听者
事件处理方法
12. 线程相关问题>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
(1) 当编译器中的线程代码运行时,可以在控制台中输入 jconsole ,来监控线程的执行情况。
(2) 线程的执行为 start 而不是 run 的原因
start 方法最终会执行其中的 run 方法,如果直接调用 run 方法来运行线程,则会没有真正的执行一个线程,而是一个串行化的
过程
6
当调用 start 方法时运行结果: 当调用重写 Thread 中的 run 方法时的运行结果为:
主线程 0 小猫喵喵喵!
小猫喵喵喵! 小猫喵喵喵!
主线程 1 小猫喵喵喵!
小猫喵喵喵! 小猫喵喵喵!
主线程 2 小猫喵喵喵!
小猫喵喵喵! 小猫喵喵喵!
返回对当前正在执行的线程对象的引用的名称: [Link]().getName()
(3)interrupt 中断非终止
void interrupt() 中断线程
例:
(这里定义每 10s 就运行 10 次输出语句,主线程输入五次后,也就是 5s,子线程被强制调用)
子线程:
for(int i=0;i<=9;i++){
[Link]("特朗普" + (++res)+[Link]().getName());
try {
[Link](10000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);
[Link]([Link]().getName() + "休息被中断了");} }
主线程:
Thread thread=new Thread(new Dog());
[Link]();
for(int i=0;i<=4;i++){
[Link]("主线程"+(++mai)
+[Link]().getName());
[Link](1000);}
[Link](); //上面进行 5 秒后中断子线程的休息,使子线程继续调用
7
(4)线程的状态分析
8
9
(5) 线程中关于互斥锁的问题
同步方法如果没有使用 static 修饰(在代码块上写):默认对象为 this(要求是同一个对象)
!!!注意事项!!!
若类继承的是 Thread 方法时,如果这时调用的是 synchronized(this)方法,且为多个线程时,这时就不再会像继承了 Runnable 接
口一样,将会导致”锁失效” 的现象
因为它会调用多个不同的对象,而不是同一个对象 运行结果
10
13. 抽象类与接口的区别>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
组成方面:
(1) 抽象类可以有构造方法,而接口不能有构造方法。
(2) 抽象类可以有普通成员变量,而接口中没有普通的成员变量。
(3) 抽象类可以有普通方法,而接口中没有普通方法等
访问修饰权限方面:
(4) 抽象类中抽象方法访问类型可以是 public/protected,但接口中的抽象方法只能是 public 类型的,并且默认为
public abstract 类型。
(5) 抽象类中的静态成员变量访问类型可以任意,但接口中定义的变量只能是 public static final 类型。
设计层面:
(6) 抽象类是对事物的抽象,描述的是一类抽象的概念,而接口的对共同行为的抽象
(7) 抽象类的一种模板式设计,若 A 为抽象类,B 和 C 都继承于 A,且他们的公共方法需要改动时,则改动时只需改动 A 即可。
(8) 接口类是一种辐射式设计,若接口进行了变更,则所有实现这个接口的类都必须进行相应的变更。
14. 关于 IO 流注意事项>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
输出流 是将数据从 编译器 -----> 文件
输入流 是将数据从 文件 -------> 编译器
(4) 序列化与反序列化:
序列化:在保存数据时,保存其数据的值和数据类型(静态 static 类不能进行序列化, 因为序列化保存的是对象的状态,
而 static 变量是属于类的状态)
反序列化:在恢复数据时,恢复数据的值和数据类型
如果需要序列化某个对象时,要实现接口 Serializable
11
运行结果:
(5) 对文件中数字等进行单独操作
主函数
这里将指定输入的字符串内容传入
实现函数
12
解决方法:
//InputStreamReader / OutputStreamWriter 是字节流通向字符流的桥梁
输出结果:
(1) 关于客户端与服务器传输信息问题
在传输信息的过程中,当前面进行了 [Link]()时,应该在输出流后面添加结束标记
不然服务器与客户端都会造成停顿,等待消息,不会结束。
voi shutdownOutput()
d 禁用此套接字的输出流。
如图所示:
(2) 关于网络上传文件
13
程序/客户端 程序/服务器
文件数据/字节
由于图片为二进制形式, 某个目录图片
磁盘上的图片
所以以字节数组的形式
进行存储
里面的具体转换方法在"D:\eclipse\[Link]" 中已实现
客户端:
这里就是将需要传入的文件转换成字节数组,然后打入到字节输出通道
(然后传入到服务端的 Socket 中)
服务器:
14
(1)这里拿到客户端打过来的数据,传入到字节数组中
(2)然后锁定需要传输新文件的位置,将之前存入的文件字节数组传入即可。
(1) netstat 指令
① netstat -an 可以查看当前主机网络的情况,包括端口监听情况和网连接情况
② Netstat -an|more 可以分页显示
说明:
(1) Listening 表示某个端口在监听
(2) 如果有一个外部程序(客户端)连接到该窗口,就会显示一条连接信息
(3) 可以输入 ctrl + c 退出指令
15
由于这里是 UDP,不存在服务器与客户端之说,只有接收端与发送端
这里的
既可以表示接收端,同时也可以表示发送端(即发送端等于接收端,接收端也等于输入端)
这里为发送端:
这里为接收端:
16
(3)这里进行拆包操作,将其 数据长度 和 数据缓冲区 取出来,进行相应的输出
18. 关于反射的要点提及>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
例:
运行结果:
这里还未曾输入就已经报错
17
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性
(3)关于类加载(面试)
连接 Linking
验证 Verification
字节码文件
Java 运 加载
(二进制形式) 初始化
[Link] 行 Loading Initialization
准备 Preparation
Javac 编译 解析 Resolution
java 源码
[Link]
18
(1) 到初始化阶段,才真正开始定义 java 程序代码,此阶段是执行 <clinit>() 方法的过程
(4)关于反射的暴破操作及相关事项
(1)解决访问私有方法的问题
(5)相关反射
类访问方法的一般用法
19
传统方法:对象 . 方法() 反射机制:方法 . 某某调用(对象)
一开始,需获取该访问类的地址:
这里,如果使用 类 . class 的方法,由于在编译阶段就要检查是否有相关的类,所以缺乏灵活性
而调用 forName ( ) 方法时,在运行阶段才会进行相关类的检查,若没有则触发异常
所以一般调用以下方法:
(6)用反射创建文件
这里为写入文件中的内容:
(2)(2)Statement 与
PrepareStatement 的执行 sql
时的用法区别
20
首先先进行建立连接:
(1)在 select 查找 方面
(3)JDBC 关于事务回滚的操作
事务定义:是用户定义的一个操作系统序列,这些操作要么全部执行,要么全部不执行, 是一个不可切分的工作单元。
由于每条单独的语句都是一个事务,每条语句后都隐含着一个
commit (默认);所以,这里首先将其设为不自动提交事务模式
回滚定义:
// void rollback()
//即在事务运行过程中当某一原子操作发生异常时,事务不能继续运行
//系统将事务已完成的操作全部撤销,将数据回滚到事务开始的状态。
21
由于在修改 sql 语句间发生了异常,所
以这里要进行回滚操作。
(4)JDBC 关于批处理
传统的批量处理:
此处理方式效率低,编译的次数多,运行的次数多
addBatch();添加需要批量处理的
sql 语句或参数
executeBatch(); 执行批量处理
语句
clearBatch(); 清空批处
理包的语句
22
20. 关于正则表达式的要点提及>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
(2)关于分组操作
这里表示为分组操作:
运行结果:
输出结果:
(4)关于分组、捕获、反向引用
23
(1)分组、捕获
这里是找出所有连续两位相同的数字 这里是找出所有连续的五位相同的数字
运行结果: 运行结果:
例:匹配若干个个位与千位相同、十位与百位相同的数,如:5225,1551
!!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!
如果正则表达式发生了变化,需要在当前语句下面重置一下
不然还停留在原来的正则表达式中,导致结果输出错误
(2)反向引用
结巴去重案例:
这里的 $1 表示的是将 (.)\\1+ 中重复出现的
数字中的第一个被分组的(.)获取到,以将所有重复的
字中替换成重复字中第一个被分组的字。
输出结果:
21. 关于数据库连接池原理
JAVA 程序
24 连接池
1. 从连接池取出连接
连接 1
2. 使用连接,操作 mysql
3. 连接放回连接池
(连接可以复用) 连接 2
等待队列
等待队列的作用:若连接池中有连接,则直接拿过来用;若连接池中没有连接,则等待其 它 JAVA 程序使用完 ”放回去”
后,再拿过来用。
这里的”放回去”是指:程序用完后,断开与其的连接,但是连接的本身还在,给下次需要使 用的程序使用。
(1)Apache 阿帕奇连接数据库的相关事项
在进行资源关闭时,不需要
将 ResultSet 与
Statement 类进
行关闭
源码:
这里 query() 方法中的底层进行了关闭
返回多行多列 sql
返回单行多列 sql
返回单行单列 sql
(2)BasicDAO 的示意图
TestDAO
1. 根据业务需求,使用对应的 DAO
BasiscDAO 2. 体现出各司其职,清晰业务
22. 关于父类与子类的向上转型的注意事项
26
(1) 向上 转型的对象
不能访问子 类新增的成
员变量
(2) 如果 子类重写了
父类的实例 方法,则上
转型调用时 一定调用了
子类重写的 实例方法
(通常)
(3) 如果 子类重写了
父类的静态 方法,那么
子类上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法(注意!!!!!!!!!!!)
27
28