原文链接:www.zxcoding.cn
java 正则表达式
用 lamda 表达式
java 导出 word
队列 栈
通过反射改变 String 类的值
java 类型转换
volatile 关键字 读写锁
LinkedBlockingQueue
PriorityQueue
CopyOnWriteArrayList
ReadWriteLock
volatile
ConcurrentHashMap
线程状态
设计模式
局部变量必须有初始值
public static String a = ""; //静态变量
public static final String b = ""; //静态常量
真数组: 数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,避免了数据覆盖的可能性,和数据类型覆盖并没有关系。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
用反射可以访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构
// 创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); // Hello World
// 1.获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
// 2.改变value属性的访问权限
valueFieldOfString.setAccessible(true);
// 3.获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
// 4.改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); // Hello_World
String str1 = "hello"; //str1指向静态区常量池
String str2 = new String("hello"); //str2指向堆上的对象
String str3 = "hello";
String str4 = new String("hello");
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str4)); //true
System.out.println(str1 == str3); //true
System.out.println(str1 == str2); //false
System.out.println(str2 == str4); //false
System.out.println(str2 == "hello"); //false
str2 = str1;
System.out.println(str2 == "hello"); //true
1. 1字节: byte , boolean
2. 2字节: short , char
3. 4字节: int , float
4. 8字节: long , double
- ### 基本数据类型的转换
1.若参与运算的数据类型不同,则先转换成同一类型,然后进行运算。
2.转换按数据长度增加的方向进行,以保证精度不降低。例如int型和long型运算时,先把int量转成long型后再进行运算。
3.所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
4.char型和short型参与运算时,必须先转换成int型。
5.在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型将转换为左边变量的类型。如果右边表达式的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度。
![image](https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZGZpbGVzLm5vd2NvZGVyLmNvbS9pbWFnZXMvMjAxNTA5MTcvNDE1NjExXzE0NDI0NTg2NjExMDZfRjRBNjJGREQyNTRGNzEwRjM5Mzc4Qzc1NEVENjVFNjE?x-oss-process=image/format,png)
private修饰的东西,只是不能在别的类中访问,但是本类中还是可以的。同时利用反射也可以做到。
1. private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
2. default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
3. protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
4. public : 对所有类可见。使用对象:类、接口、变量、方法
![image](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0pvdXJXb24vaW1hZ2UvbWFzdGVyL0phdmElRTUlOUYlQkElRTclQTElODAlRTglQUYlQUQlRTYlQjMlOTUvSmF2YSVFOCVBRSVCRiVFOSU5NyVBRSVFNCVCRiVBRSVFOSVBNSVCMCVFNyVBQyVBNi5wbmc?x-oss-process=image/format,png)
1. final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值
2. finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块
中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
3. finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调
用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
最后判断。
自动装箱都是通过包装类的 valueOf()方法来实现的.自动拆箱都是通过包装类对象的 xxxValue()来实现的
Integer integer=1; //装箱 Integer integer=Integer.valueOf(1);
int i=integer; //拆箱 int i=integer.intValue();
包装对象的数值比较,不能简单的使用==,虽然-128 到 127 之间的数字可以,但是这个范围之外还是需要使用 equals 比较
前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为 null,那么自动拆箱时就有可能抛出 NPE
如果一个 for 循环中有大量拆装箱操作,会浪费很多资源
有 ByteCache 用于缓存 Byte 对象
有 ShortCache 用于缓存 Short 对象
有 LongCache 用于缓存 Long 对象
有 CharacterCache 用于缓存 Character 对象
有 IntegerCache 用户缓存 Integer 对象:-128 到 127.可以通过 XX:AutoBoxCacheMax=size 或者
java.lang.Integer.IntegerCache.high 修改
Byte, Short, Long 有固定范围: -128 到 127
对于 Character, 范围是 0 到 127.除了 Integer 以外,这个范围都不能改变.
List 接口和 Set 接口都继承自 Collection 接口,Collection 接口继承 Iterable 接口(Iterable 有一个 Iterator 方法),即可迭代的
Collection 只能存储引用类型,并且是单个存储
List 接口存储元素特点:有序(存进去什么顺序取出来还什么顺序),可重复
Set 接口存储元素特点:无序,不可重复
实现 List 接口主要的类包括 ArrayList,LinkedList,Vector;实现 Set 的主要类包括:hashSet,另外还有一个 TreeSet 接口继承它(自动排序)
Map 接口以键值对方式存储元素,键无序不可重复,Map 和 Collection 没有任何关系
Collection 是一个集合接口. 它提供了对集合对象进行基本操作的通用接口方法.Collection 接口在 Java 类库中有很多具体的实现.是 list,set 等的父接口.
Collections 是一个包装类. 它包含有各种有关集合操作的静态多态方法.此类不能实例化,就像一个工具类,服务于 Java 的 Collection 框架.
List,Set 都是继承自 Collection 接口.都是用来存储一组相同类型的元素的.
不同:
List 特点:元素有放入顺序,元素可重复.有顺序,即先放入的元素排在前面
Set 特点:元素无放入顺序,元素不可重复.
ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构
这三者都实现了 List 接口,使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率.
Vector 和 ArrayList 在更多元素添加进来时会请求更大的空间.Vector 每次请求其大小的双倍空间,而 ArrayList 每次对 size 增长 50%.
而 LinkedList 还实现了 Queue 接口,该接口比 List 提供了更多的方法,包括 offer(),peek(),poll()等.
TreeSet 是二叉树实现的,Treeset中的数据是自动排好序的,不允许放入null值 2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束
TreeSet的底层是TreeMap的keySet(),而TreeMap是基于红黑树实现的,红黑树是一种平衡二叉查找树,它能保证任何一个节点的左右子树的高度差不会超过较矮的那棵的一倍.
在HashSet中,基本的操作都是有HashMap底层实现的,因为HashSet底层是用HashMap存储数据的.当向HashSet中添加元素的时候,首先计算元素的hashcode值,然后通过扰动计算和按位与的方式计算出这个元素的存储位置,如果这个位置位空,就将元素添加进去;如果不为空,则用equals方法比较元素是否相等,相等就不添加,否则找一个空位添加.(HashMap 支持key=null 但是 Hashtable 不支持 key =null)
TreeMap是按key排序的,元素在插入TreeSet时compareTo()方法要被调用,所以TreeSet中的元素要实现Comparable接口.TreeSet作为一种Set,它不允许出现重复元素.TreeSet是用compareTo()来判断重复元素的.
1. 线程安全
HashTable 中的方法是同步的,而HashMap中的方法在默认情况下是非同步的.在多线程并发的环境下,可以直接使用HashTable,但是要使用HashMap的话就要自己增加同步处理了
2. 继承关系
HashTable是基于陈旧的Dictionary类继承来的. HashMap继承的抽象类AbstractMap实现了Map接口
3. 允不允许null值
HashTable中,key和value都不允许出现null值,否则会抛出NullPointerException异常. HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null
4. 默认初始容量和扩容机制
HashTable中的hash数组初始大小是11,增加的方式是 old*2+1.HashMap中hash数组的默认大小是16,而且一定是2的指数
5. 哈希值的使用不同
HashTable直接使用对象的hashCode. HashMap重新计算hash值
6. 遍历方式的内部实现上不同
Hashtable、HashMap都使用了 Iterator.而由于历史原因,Hashtable还使用了Enumeration的方式 . HashMap 实现 Iterator,支持fast-fail,Hashtable的 Iterator 遍历支持fast-fail,用 Enumeration 不支持 fast-fail
ConcurrentHashMap和HashMap的实现方式不一样,虽然都是使用桶数组实现的,但是还是有区别,ConcurrentHashMap对桶数组进行了分段,而HashMap并没有
ConcurrentHashMap在每一个分段上都用锁进行了保护.HashMap没有锁机制.所以,前者线程安全的,后者不是线程安全的
CopyOnWriteArrayList 适用于写少读多的并发场景
ReadWriteLock 即为读写锁,他要求写与写之间互斥,读与写之间互斥,
读与读之间可以并发执行在读多写少的情况下可以提高效率
ConcurrentHashMap 是同步的 HashMap,读写都加锁
volatile 只保证多线程操作的可见性,不保证原子性
抽象类是可以实现接口的,而且抽象类也可以继承自抽象类
抽象类指有 abstract 修饰的 class,其可以包含抽象方法,也可以不包含
抽象类和接口都是不能被实例化的,只有具体的类才可以被实例化
抽象类和接口
相同点:都不能被实例化,位于继承树的顶端,都包含抽象方法
不同点:
native 关键字表名修饰的方法是由其它非 Java 语言编写的
final 修饰符,可以修饰类,方法,变量,不能修饰接口
final 修饰的类不可以被继承
final 修饰的方法不可以被覆盖
final 修饰的变量为常量只能赋值一次
一般 final 化的成员变量也会静态化
java 和 C++都有三个特征:封装、继承和多态
如果父类中只有有参构造函数,则子类构造函数必须先显示调用即父类没有无参的构造函数,所以子类需要在自己的构造函数中显式调用父类的构造函数,
执行父类的带参构造前要先对父类中的对象进行初始化
在 Java 中,可以将一个类定义在另一个类里面或者一个方法里边,这样的类称为内部类,广泛意义上的内部类一般包括四种:成员内部类,局部内部类,匿名内部类,静态内部类
该类像是外部类的一个成员,可以无条件的访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员);
成员内部类拥有与外部类同名的成员变量时,会发生隐藏现象,即默认情况下访问的是成员内部类中的成员如果要访问外部类中的成员,需要以下形式访问:【外部类.this.成员变量 或 外部类.this.成员方法】;
在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问;
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象;创建方法为 外部类实例.new 内部类()
内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限如果成员内部类用 private 修饰,则只能在外部类的内部访问;如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则 只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问外部类只能被 public 和包访问两种权限修饰
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
Circle circle=new Circle();
Circle.Draw draw=circle.new Draw(); //创建成员内部类实例
draw.drawSahpe();
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内;
局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
一般使用匿名内部类的方法来编写事件监听代码; 匿名内部类必须继承或实现一个已有的接口
匿名内部类是不能有访问修饰符和 static 修饰符的;
匿名内部类是唯一一种没有构造器的类;
匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写
history_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似;
不能使用外部类的非 static 成员变量或者方法
内部类可以是静态 static 的,也可用 public,default,protected 和 private 修饰,
外部类的修饰符只能是 public,abstract,final
静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类()
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
用 start方法来启动线程,是真正实现了多线程, 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,
并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。
Person person=(Person) Class.forName("com.Person").newInstance(); //调用无参构造函数
Constructor constructor=Person.class.getConstructor (); //调用有参构造函数
Person person=constructor.newInstance();
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
Class.newInstance() 抛出所有由被调用构造函数抛出的异常。
Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的;
Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数
异常分为 Error 和 Exception。Exception 和 Error, ⼆者都是 Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。均继承自 Throwable 类。
public void test() throw new Exception{ }
这种异常在 IO 操作中⽐较多。 ⽐如 FileNotFoundException , 当我们使⽤ IO 流处理⼀个⽂件的时候, 有⼀种特殊情况, 就是⽂件不存在, 所以, 在⽂件处理的接⼜定义时他会显⽰抛出 FileNotFoundException, ⽬的就是告诉这个⽅法的调⽤者,我这个⽅法不保证⼀定可以成功, 是有可能找不到对应的⽂件 的, 你要明确的对这种情况做特殊处理哦。 B extends A
B b = (B) new A(); // 子类的引用 执行父类的对象 需要强转
A a = new B(); //里氏替换原则 可以用父类的地方都可以用子类
存放了编译时期生成的各种字面量
存放编译时期生成的符号引用
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
类和接口的全限定名
字段名称和描述符
方法名称和描述符
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池
运行时常量池大小受方法区大小的影响
内存泄露:在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存. 内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费.
以下代码会导致内存泄漏:
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3);
str = null;
原因:
这段简单的程序有两个字符串变量 str、sub。sub 字符串是由父字符串 str 截取得到的,假如上述这段程序在 JDK1.6 中运行,我们知道数组的内存空间分配是在堆上进行的,那么 sub 和 str 的内部 char 数组 value 是公用了同一个,也就是上述有字符 a~字符 t 组成的 char 数组,str 和 sub 唯一的差别就是在数组中其实 beginIndex 和字符长度 count 的不同。在第三句,我们使 str 引用为空,本意是释放 str 占用的空间,但是这个时候,GC 是无法回收这个大的 char 数组的,因为还在被 sub 字符串内部引用着,虽然 sub 只截取这个大数组的一小部分。当 str 是一个非常大字符串的时候,这种浪费是非常明显的,甚至会带来性能问题,解决这个问题可以是通过以下的方法:
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3) + "";
/*利用的就是字符串的拼接技术,它会创建一个
新的字符串,这个新的字符串会使用一个新的内部char数组存储自己实际需要的字符,这样父数
组的char数组就不会被其他引用,令str=null,在下一次GC回收的时候会回收整个str占用的空间*/
str = null;
1.7: 在 jdk 7 中得到解决.在 jdk 7 中,substring 方法会在堆内存中创建一个新的数组
String i1 = (new StringBuilder()).append(i).toString();
transient 关键字的作⽤是控制变量的序列化, 在变量声明前加上该关键字, 可以阻⽌该变量被序列化到⽂件中, 在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
volatile 能保证数据的可见性,但不能完全保证数据的原子性,synchronized 即保证了数据的可见性也保证了原子性
操作 byte 类型数据,主要操作类是 OutputStream、InputStream 的子类;不用缓冲区,直接对文件本身操作
流对象是字节流的类有:FileInputStream
BufferedInputStream
PushbackInputStream
ByteArrayInputStream
操作字符类型数据,主要操作类是 Reader、Writer 的子类;使用缓冲区缓冲字符,不关闭流就不会输出任何内容
BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解。
NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4 开始支持。
AIO 方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。
Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。
反射机制指的是程序在运行时能够获取自身的信息
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
getDeclaredField是可以获取一个类的所有字段.
getField只能获取类的public 字段
String s1 = new String("AAA");
String s2 = s1.intern();
String s3 = "AAA";
System.out.println(s2);//AAA
System.out.println(s1 == s2);//false,因为一个是堆内存中的String对象,一个是常量池中的String对象,
System.out.println(s2 == s3);//true, s1,s2指向常量池中的”AAA“
String a = "a";
String b = "b";
String str1 = "a" + "b";//常量池中的对象
String str2 = a + b; //在堆上创建的新的对象
String str3 = "ab";//常量池中的对象
System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//true
System.out.println(str2 == str3);//false