1.基本数据类型
整数类型:byte,short,int,long
浮点数类型:float,double
字符类型:char
布尔类型:boolean
计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。它的二进制表示范围从00000000到11111111,换算成十进制是0到255,换算成十六进制是00~ff。
基本数据类型占用字节数及范围。
byte:1个字节,-128 ~ 127(2^7-1)。
short:2个字节, -32768 ~ 32767(2^15-1)。
int:4个字节,-2147483648 ~ 2147483647(2^31-1)。
long:8个字节,-9223372036854775808 ~ 9223372036854775807(2^63-1)。
float:4个字节,可表示的最大值是3.4x1038。
double:8个字节,可表示的最大值是1.79x10308。
char:2个字节,Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符。
2.字符串
2.1.字符串和编码
当我们想要比较两个字符串是否相同时,要特别注意,我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用==。
public static void main(String[] args) {
String s = "hello";
String s2 = "hello";
System.out.println(s == s2);
}
运行结果为true。从表面上看,两个字符串用==和equals()比较都为true,但实际上那只是Java编译器在编译期,会自动把所有相同的字符串当作一个对象放入常量池,自然s1和s2的引用就是相同的。结论:两个字符串比较,必须总是使用equals()方法。
使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n。trim()并没有改变字符串的内容,而是返回了一个新字符串。
" \tHello\r\n ".trim(); // "Hello"
替换字符串
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"
要分割字符串,使用split()方法,并且传入的也是正则表达式:
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}
拼接字符串使用静态方法join(),它用指定的字符串连接字符串数组:
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"
要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()。这是一个重载方法,编译器会根据参数自动选择合适的方法:
tring.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c
要把字符串转换为其他类型,就需要根据情况。
int n1 = Integer.parseInt("123"); // 123
boolean b1 = Boolean.parseBoolean("true"); // true
String和char[]类型可以互相转换,方法是:
char[] cs = "Hello".toCharArray(); // String -> char[]
String s = new String(cs); // char[] -> String
2.2.StringBuilder
考察下面的循环代码:
String s = "";
for (int i = 0; i < 1000; i++) {
s = s + "," + i;
}
虽然可以直接拼接字符串,但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象:
StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
sb.append(',');
sb.append(i);
}
String s = sb.toString();
用StringJoiner的结果少了前面的"Hello "和结尾的"!"!遇到这种情况,需要给StringJoiner指定“开头”和“结尾”
public static void main(String[] args) {
String[] arr = {"Bob","Jessie"};
StringJoiner stringJoiner = new StringJoiner(",","Hello ","!");
for(String name:arr){
stringJoiner.add(name);
}
System.out.println(stringJoiner.toString());
}
运行结果:
Hello Bob,Jessie!
3.枚举类
在Java中,我们可以通过static final来定义常量。例如,我们希望定义周一到周日这7个常量,可以用7个不同的int表示:
public class Weekday {
public static final int SUN = 0;
public static final int MON = 1;
public static final int TUE = 2;
public static final int WED = 3;
public static final int THU = 4;
public static final int FRI = 5;
public static final int SAT = 6;
}
使用常量的时候,可以这么引用:
if (day == Weekday.SAT || day == Weekday.SUN) {
// TODO: work at home
}
使用这些常量来表示一组枚举值的时候,有一个严重的问题就是,编译器无法检查每个值的合理性。
为了让编译器能自动检查某个值在枚举的集合内,并且,不同用途的枚举需要不同的类型来标记,不能混用,我们可以使用enum来定义枚举类:
public static void main(String[] args) {
WeekDay day = WeekDay.SAT;
if (day == WeekDay.SAT){
System.out.println("play");
}else {
System.out.println("work");
}
}
enum WeekDay{
MON,TUE,WED,THU,FRI,SAT,SUN;
}
enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==比较:
if (day == Weekday.FRI) { // ok!
}
if (day.equals(Weekday.SUN)) { // ok, but more code!
}
它有以下几个特点:
-定义的enum类型总是继承自java.lang.Enum,且无法被继承;
-只能定义出enum的实例,而无法通过new操作符创建enum的实例;
-定义的每个实例都是引用类型的唯一实例;
-可以将enum类型用于switch语句。
可以通过name()返回常量名:
String s = Weekday.SUN.name(); // "SUN"
用于swich语句
public static void main(String[] args) {
WeekDay day = WeekDay.SAT;
switch (day){
case SAT:
break;
case MON:
break;
default:
break;
}
}
enum WeekDay{
MON,TUE,WED,THU,FRI,SAT,SUN;
}
可以为enum编写构造方法、字段和方法。
public static void main(String[] args) {
WeekDay day = WeekDay.SAT;
System.out.println(day.toString());
}
enum WeekDay{
MON(1,"周一"),TUE(2,"周二"),WED(3,"周三"),THU(4,"周四"),FRI(5,"周五"),SAT(6,"周六"),SUN(7,"周日");
private final int dayValue;
private final String Chinese;
private WeekDay(int dayValue,String chinese){
this.dayValue = dayValue;
this.Chinese = chinese;
}
@Override
public String toString() {
return Chinese;
}
}
运行结果:
周六
注意:枚举类的字段也可以是非final类型,即可以在运行期修改,但是不推荐这样做!enum的构造方法要声明为private。
4.常用工具类
4.1.Math
顾名思义,Math类就是用来进行数学计算的,它提供了大量的静态方法来便于我们实现数学计算:
//求绝对值:
Math.abs(-100); // 100
Math.abs(-7.8); // 7.8
//取最大或最小值:
Math.max(100, 99); // 100
Math.min(1.2, 2.3); // 1.2
//计算xy次方:
Math.pow(2, 10); // 2的10次方=1024
//计算√x:
Math.sqrt(2); // 1.414...
//计算ex次方:
Math.exp(2); // 7.389...
//计算以e为底的对数:
Math.log(4); // 1.386...
//计算以10为底的对数:
Math.log10(100); // 2
//三角函数:
Math.sin(3.14); // 0.00159...
Math.cos(3.14); // -0.9999...
Math.tan(3.14); // -0.0015...
Math.asin(1.0); // 1.57079...
Math.acos(1.0); // 0.0
//Math还提供了几个数学常量:
double pi = Math.PI; // 3.14159...
double e = Math.E; // 2.7182818...
Math.sin(Math.PI / 6); // sin(π/6) = 0.5
//生成一个随机数x,x的范围是0 <= x < 1:
Math.random(); // 0.53907... 每次都不一样
4.2.Random
Random用来创建伪随机数。所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的。
要生成一个随机数,可以使用nextInt()、nextLong()、nextFloat()、nextDouble():
Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double
创建Random实例时,如果不给定种子,就使用系统当前时间戳作为种子,因此每次运行时,种子不同,得到的伪随机数序列就不同。
如果我们在创建Random实例时指定一个种子,就会得到完全确定的随机数序列。
4.3SecureRandom
实际上真正的真随机数只能通过量子力学原理来获取,而我们想要的是一个不可预测的安全的随机数,SecureRandom就是用来创建安全的随机数的:
SecureRandom sr = new SecureRandom();
System.out.println(sr.nextInt(100));
SecureRandom的安全性是通过操作系统提供的安全的随机种子来生成随机数。这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”。需要使用安全随机数的时候,必须使用SecureRandom,绝不能使用Random!
网友评论