常用基础类
一)String
String内部用一个字符数组表示字符串:
private final char value[];
注意:Java9对此做了优化,采用byte[],如果字符都是ASCII字符,它就可以使用一个字节表示一个字符。
String有的两个构造方法,可以根据参数创建String变量:
//根据参数创建一个新的char数组, //并使用新的char数组实现String的大部分方法 public String(char value[]) public String(char value[], int offset, int count)
编码相关:
返回字符串给定编码的字节表示:
public byte[] getBytes() //使用系统默认编码 public byte[] getBytes(String charsetName) public byte[] getBytes(Charset charset)
根据字节和编码创建字符串的构造方法:
public String(byte bytes[], int offset, int length, String charsetName) public String(byte bytes[], Charset charset)
不可变的String:
String定义为不可变的程序可以更简单、安全、容易理解。
常量字符串:
Java中的字符串常量就是String对象,可以调用String的方法。
在内存中它们被放入一个共享的地方,称为字符串常量池,它保存
所有的常量字符串,每个常量只保存一份,被所有使用者共享。
当通过常量的形式使用字符串的时候,就是使用常量池中的String类型对象。
String a = "apple"; String b = "apple"; System.out.println(a == b); //true //上面的代码实际上相当于 String c = new String(new char[]{'a', 'p', 'p', 'l', 'e'}); String e = c; String d = c; //实际上只有一个String对象,三个变量都指向这个变量 System.out.println(e == d); //true //注意:如果不是通过常量赋值,而是通过new创建,就会不一样 String f = new String("apple"); String g = new String("apple"); System.out.println(f == g);//false 此时比较的是引用地址当然false
因为,String的构造方法:
public String(String original) { this.value = original.value; this.hash = original.hash; }
hash是String的一个实例变量,表示缓存的hashCode值
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
String与正则:
public String[] split(String regex) public boolean matches(String regex) public String replaceFirst(String regex, String replacement) public String replaceAll(String regex, String replacement)
二)StringBuilder
StringBuffer是线程安全的而StringBuilder不是,但是注意线程安全是有成本的。
1.基本实现原理
StringBuilder父类AbstractStringBuilder封装了一个字符数组:
char value[];
与String不同的是,它不是final的可以被修改,它有一个实例变量表示数组中已经使用的字符个数:
int count;
抽象类的构造方法:
AbstractStringBuilder(int capacity) { value = new char[capacity]; }
StringBuilder的构造方法:
public StringBuilder() { super(16); }
public StringBuilder(int capacity) { super(capacity); }
public StringBuilder(String str) { super(str.length() + 16); append(str); }
append方法:
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); //确保数组的长度足以容纳新添加的字符 ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
private void ensureCapacityInternal(int minimumCapacity) { // 如果数组长度小于需要的长度,则进行扩展 if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; }
注意:为了避免每次调用append方法就进行内存分配,指数扩展策略。
在不知道最终需要多长(不知道下次append需要多长)的情况下
,指数扩展策略广泛应用于各种计算机内存分配相关的计算中。
toString方法:
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
String的 + 和 += 运算符:
当String使用这两个运算符的时候,Java编译器一般会生成StringBuilder,
并调用append方法。例如:
String hello = "hello"; hello+=",world"; System.out.println(hello); //编译器会转换为: StringBuilder hello = new StringBuilder("hello"); hello.append(",world"); System.out.println(hello.toString());
既然编译器调用了StringBuilder的append方法,那还有什么必要直接使用StringBuilder呢?
因为在复杂的情况下编译器可能会生成过多的StringBuilder。
String hello = "hello"; for(int i=0;i<3;i++){ hello+=",world"; } System.out.println(hello); //编译器转换: /*String hello = "hello"; for(int i=0;i<3;i++){ StringBuilder sb = new StringBuilder(hello); sb.append(",world"); hello = sb.toString(); } System.out.println(hello);*/
三)Arrays
数组的优点是比容器的效率更高。
Arrays类包含包含对数组操作的静态方法。
1.toString
输出一个数组的字符串形式,有9个重载方法:
public static String toString(int[] a) public static String toString(Object[] a)
2.排序
sort()方法:
//每总基本类型都有 public static void sort(int[] a) public static void sort(double[] a) //引用类型:但对象要求实现Comparable接口 public static void sort(Object[] a) public static void sort(Object[] a, int fromIndex, int toIndex) //或者 public static <T> void sort(T[] a, Comparator<? super T> c) public static <T> void sort(T[] a, int fromIndex, int toIndex,Comparator<? super T> c)
3.查找
可在已排序的数组中进行二分查找(从中间开始找......):
public static int binarySearch(int[] a, int key) public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) public static int binarySearch(Object[] a, Object key) //自定义比较器,需要和排序时的比较器一致 public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
5.多维数组
本质:数组中的每个元素是另一个数组,层层嵌套。
创建多维数组:
int[][][] arr = new int[10][10][10]; int[][] arr = new int[2][]; arr[0] = new int[3]; arr[1] = new int[5];
Arrays数组中的方法都有针对多维数组的方法:
public static String deepToString(Object[] a) public static boolean deepEquals(Object[] a1, Object[] a2) public static int deepHashCode(Object a[])
四)日期和时间
由于旧的API被广泛使用,这里介绍Java8之前的日期和时间API
1.基本概念
1)时区
全球一共24个时区,英国格林尼治是0时区,北京是东八区,
0时区也被称为GMT+0时间,GMT是格林尼治标准时间,北京时间就是GMT+8:00。
2)时刻和纪元
所有计算机内部都用一个整数表示时刻,这个整数是距离格林尼治标准时间1970年1月1日0时0分
的毫秒数。这个时间也被称为Epoch Time(纪元时).这个整数表示的是一个时刻,与时区无关,世界
上各个地方都是同一时刻,但各个地区对这一时刻的解读(如年月日时分)可能是不一样的。
2.时间和日期API(Java8以前的)
Date: 表示时刻,即绝对时间,与年月日无关。
Calender:表示年历,Calender是一个抽象类,其中表示公历的子类为Gregorian-Calender。
DateForm:表示格式化,能够将日期和时间与字符串间进行转换,该类也是一个抽象类,最
常用的子类是SimpleDateForm。
TimeZone:表示时区。
Locale:表示国家(地区)和语言。
1)Date
内部用fast变量表示时刻:
private transient long fastTime;//表示距离纪元时的毫秒数
有两个构造方法:
public Date() { this(System.currentTimeMillis()); } public Date(long date) { fastTime = date; }
Date中的大部分方法都过时,其中没有过时的有:
public long getTime()//返回毫秒数 public boolean equals(Object obj)//主要是比较内部毫秒数是否相同 public int compareTo(Date anotherDate)//与其他毫秒数进行比较 public boolean before(Date when) public boolean after(Date when) public int hashCode()
2)TimeZone
TimeZone defaultTimeZon = TimeZone.getDefault(); System.out.println(defaultTimeZon.getID()); //Asia/Shanghai TimeZone usTimeZone = TimeZone.getTimeZone("US/Eastern"); System.out.println(usTimeZone.getID()); //US/Eastern TimeZone unknownTimeZone = TimeZone.getTimeZone("GMT+09:00"); System.out.println(unknownTimeZone.getID()); //GMT+09:00
3)Locale
它主要有两个参数:一个是国家或地区,一个是语言
Locale类中定义了一些静态变量,表示常见的Locale,例如:
System.out.println(Locale.US); //en_US System.out.println(Locale.ENGLISH); //en System.out.println(Locale.TAIWAN); //zh_TW System.out.println(Locale.CHINESE); //zh System.out.println(Locale.CHINA); //zh_CN System.out.println(Locale.SIMPLIFIED_CHINESE); //zh_CN
4)Calendar
该类是日期和时间操作的主要类,它表示与TimeZome和Locale相关的日历信息。
Calendar内部也有一个表示时刻的毫秒数:
protected long time;
还有一个数组表示日历中各个字段的值
protected int fields[];//这个数组长度为17,保存一个日期中各个字段的值
Calendar定义了一些静态变量,表示这些字段如:
Calendar.YEAR表示年
Calender是抽象类,不能直接创建实例:
public static Calendar getInstance() public static Calendar getInstance(TimeZone zone, Locale aLocale) //根据提供的TimeZone和Locale创建Calender子类对象
内部,Calendar会将表示时刻的毫秒数,按照TimeZone和Locale对应的年历,
计算各个日历字段的值,存放在fields数组中。
Calendar calendar = Calendar.getInstance(Locale.CHINA); System.out.println("YEAR: " + calendar.get(Calendar.YEAR)); //2018 System.out.println("MONTH: " + calendar.get(Calendar.MONTH)); //10 System.out.println("DAY: " + calendar.get(Calendar.DAY_OF_MONTH)); //13 System.out.println("Day of week: " + calendar.get(Calendar.DAY_OF_WEEK)); //3
Calendar支持Date或者毫秒数设置时间:
public final void setTime(Date date) public void setTimeInMillis(long millis)
也可根据年月日设置:
public final void set(int year, int month, int date) public final void set(int year, int month, int date,int hourOfDay, int minute, int second) public void set(int field, int value)
直接增加或者减少时间:
public void add(int field, int amount)//amount正数表示增加,负数表示减少
Calendar之间也可以进行比较:
public boolean equals(Object obj) public int compareTo(Calendar anotherCalendar) public boolean after(Object when) public boolean before(Object when)
5)DateForm
两个主要方法:
public final String format(Date date) public Date parse(String source)
DateForm定义了4个静态变量,表示4种风格,不同的风格输出详尽程度不一样。
DateFrom是抽象类,用工厂方法创建实例:
public final static DateFormat getDateTimeInstance() public final static DateFormat getDateInstance() public final static DateFormat getTimeInstance()
重载方法接收日期及时间风格和Locale为参数:
DateFormat getDateTimeInstance(int dateStyle, int timeStyle) DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
Calendar calendar = Calendar.getInstance(); calendar.set(2018, 11, 15, 8, 22); System.out.println(DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, Locale.CHINESE).format(calendar.getTime())); //2018年12月15日 上午8:22
DateForm设置TimeZone:
public void setTimeZone(TimeZone zone)
6)SimpleDateForm
该类是DateForm的子类,与父类的主要不同是,它可以接收一个自定义模式作为参数
Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 E HH时mm分ss秒"); System.out.println("Now is : " + format.format(calendar.getTime())); //Now is : 2018年11月14日 星期三 09时31分29秒
其中E表示星期,hh也可表示小时,不过是12小时制的。
3.Java8之前时间和日期API的局限性
1)Date种过时的方法
2)Calendar操作繁琐
API设计失败,要写很多代码,难以计算两个日期之间,比如有几天几个月等等。
3)DateForm的线程安全问题
五)随机
1.Math.random
该静态方法生成一个0到1的随机数(duoble类型),但不包括1和0.
for (int i = 2; i >= 0; i--) { System.out.println(Math.random()); /*0.2031695860558771 0.7189188014572521 0.686824037093907 */ //每次随机都不一样哦 }
该方法的实现:
public static double random() { Random rnd = randomNumberGenerator; if (rnd == null) rnd = initRNG(); return rnd.nextDouble(); }
2.Random类
该类提供了丰富的随机方法:
Random random = new Random(); System.out.println(random.nextInt()); System.out.println(random.nextInt(100)); //-809244560 //87
nextInt()产生一个随机的int,可能为正数,也可能为负数,nextInt(100)产生随机int,范围为0-100
除了默认的构造方法还有一个接收long类型的种子参数的构造方法:
public Random(long seed)
种子决定了随机产生的序列,种子相同,产生的随机数序列就是相同的。
Random random = new Random(20160824); for (int i = 0; i < 5; i++) { System.out.println(random.nextInt(100)); }
结果为:69 13 13 94 50
这个程序无论执行多少遍,在哪儿执行,结果都是一样的。
在Random类中还可用synchronized public void setSeed(long seed) 方法指定种子。
指定种子是为了实现可重复随机。
如果Random使用默认的构造函数,不传入种子,他会自动生成一个种子,
这个种子数是真正的随机数。
3.随机数的应用
1)随机密码
//生成6位数字随机密码 public static String randomPassWord() { char[] chars = new char[6]; Random random = new Random(); for (int i = 0; i < 6; i++) { chars[i] = (char)(random.nextInt(10) + '0'); } return new String(chars); }
生成8位随机验证码,可能包含数字、特殊字符、字母
private static final String SPECIAL_CHARS = "!@#$%^&*-=_+"; private static char nextChar(Random rnd) { switch (rnd.nextInt(4)) { case 0: return (char) ('a' + rnd.nextInt(26)); case 1: return (char) ('A' + rnd.nextInt(26)); case 2: return (char) ('0' + rnd.nextInt(10)); default: return SPECIAL_CHARS.charAt(rnd.nextInt(SPECIAL_CHARS.length())); } } //生成8位随机密码 public static String randomPassWord() { char[] chars = new char[8]; Random random = new Random(); for (int i = 0; i < 8; i++) { chars[i] = nextChar(random); } return new String(chars); }