1、昨日复习
画图说明线程的生命周期,以及各状态切换使用到的方法等状态,方法
同步代码块中涉及到同步监视器和共享数据,谈谈你对同步监视器和共享数据的理解,以及注意点。
synchronized(同步监视器){
//操作共享数据的代码(不能包括多了,也不能包括少了)
}sleep()和wait()的区别
写一个线程安全的懒汉式
class Bank{
private Bank(){
}
private static Bank instance = null;
public static Bank getInstance(){
//方式一
/* synchronized (Bank.class){
if (instance == null){
instance = new Bank();
}
return instance;
}*/
if (instance == null){
synchronized (Bank.class){
if (instance == null){
instance = new Bank();
}
}
}
return instance;
}
}
- 创建多线程有哪几种方式:4种
继承Thread类
实现Runnable接口
实现Callable接口
线程池(响应速度提高了,提高了资源的重用率,便于管理)2、String类
String的特性
String类:代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
String是一个final类,代表不可变的字符序列。
字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
String对象的字符内容是存储在一个字符数组value[]中的。
1、当前字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
2、当对现有的字符串进行连续操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
3、当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
@Test
public void test2() {
//通过字面量定义的方式:此时的s1和s2的数据JavaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new+构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
}
面试题:String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:“abc”
@Test
public void test3() {
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
}
结论:
1、常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
2、只要其中有一个是变量,结果就在堆中(相当于new了一个)
3、如果拼接的结果调用intern()方法,返回值就在常量池中
//面试题
package com.atguigu.java2;
public class StringTest {
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");//good and
System.out.println(ex.ch);//best
}
}
3、String常用方法
复习:
String 与基本数据类型、包装类之间的转换。
String—->基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)
基本数据类型、包装类—->String:调用String重载的valueOf(xxx)
String与char[]之间的转换
String—->char[]:调用String的toCharArray()
char[]—->String:调用String的构造器
String与byte[]之间的转换
String—->byte[]:调用String的getBytes()
byte[]—->String:调用String的构造器
4、StringBuffer和StringBuider的使用
String、StringBuffer、StringBuider三者的异同?
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列,线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;线程不安全的,效率高;jdk5.0新增的;底层使用char[]存储
源码分析:
String str = new String();//char[] value = new char[0];
String str1 = new String();//char[] value = new char[0];
StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。
sb1.append(‘a’);//value[0]=’a’;
sb1.append(‘b’);//value[1]=’b’;
StringBuffer sb2 = new StringBuffer(“abc”);//char[] value = new char[“abc”.length()+16]
扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍+2,同时将原有的数组中的元素复制到新的数组中。
指导意义:开发中建议大家使用:StringBuffer(int capacity)或StringBuilder(int capacity);
方法中范围只要涉及到开始和结束的都是左闭右开(左边是闭区间,右边是开区间)
总结:
增:append(xxx);
删:delete(int start,int end);
改:setCharAt(int n,char ch)/replace(int start,int end,String str)
查:charAt(int n)
插:insert(int offset,xxx)
长度:length();
遍历:for()+charAt();/toString();
StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样
public void test2() {
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
5、JDK8之前日期时间API
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
此方法适于计算时间差。
java.util.Date类
|——-java.sql.Date类
1、两个构造器的使用
构造器一:new Date();创建一个对应当前时间的Date对象
构造器二:new Date(long time);创建指定毫秒数time的Date对象。
2、两个方法的使用
>toString():显示当前的年、月、日、时、分、秒
>getTime():获取当前Date对象对应的毫秒数。
3、java.sql.Date类对应着数据库中的日期类型的变量
>如何实例化? new Date(long time);创建指定毫秒数time的Date对象。
>如何将java.util.Date对象转换为java.sql.Date对象
情况一:多态中子父类中的强转
情况二:Date date= new Date();
java.sql.Date date1 = new java.sql.Date(date.getTime());
尚硅谷宋红康第9章_Java常用类.pdf