一、系统相关

1、System

  • 获取平台信息

System类提供获取当前Java程序的运行平台相关信息,在程序中不能创建System类的对象,可以通过调用此类的静态方法获取这些信息。

//系统环境变量
Map<String, String> env = System.getenv();
//系统属性
Properties props = System.getProperties();

//获取指定的环境变量或系统属性
System.getenv("JAVA_HOME");//C:\Program Files\Java\jdk1.8.0_45
System.getenv("OS");//Windows_NT
System.getProperty("os.name");//Windows 8.1
  • 输入&输出

可以使用System.in(InputStream)、System.out(PrintStream)、System.err(PrintStream)获取标准输入、标准输出和错误输出流,而且可以使用System.setIn()System.setOut()System.setErr()方法来改变系统的标准输入、输出和错误输出流。

static PrintStream err
static InputStream in
static PrintStream out
static void setErr(PrintStream err)
static void setIn(InputStream in)
static void setOut(PrintStream out)
  • 获取时间

调用System.currentTimeMillis()(以毫秒为单位)与System.nanoTime()(以微秒为单位)方法的返回值是当前时间与UTC 1970年1月1日午夜的时间差。

//Date:20180902
System.out.println(System.currentTimeMillis());//1535859644472
System.out.println(System.nanoTime());//98770354993078
  • 获取对象的hashCode

可以使用System.identityHashCode(Object x)方法获取指定对象的hashCode值(根据此对象的地址计算),如果某个类的hashCode()方法被重写,仍然可以使用此方法获取唯一标识该对象的hashCode值。

class User{
	@Override
	public int hashCode() {
		return 666;
	}
}

User user = new User();
System.out.println(user.hashCode());
System.out.println(System.identityHashCode(user));

程序输出:

666
1704856573
  • 垃圾回收

可以调用System.gc()通知系统进行垃圾回收,调用System.runFinalization()通知系统进行资源清理。

2、Runtime

Runtime类代表Java程序的运行时环境,每个Java程序都有一个对应的Runtime对象,可通过此对象来获得运行时的环境信息(处理器、内存等):

Runtime runtime = Runtime.getRuntime();
//处理器
System.out.println(runtime.availableProcessors());//4
//JVM可使用的总内存,以字节为单位
System.out.println(runtime.totalMemory());//128974848
//JVM空闲内存
System.out.println(runtime.freeMemory());//126929904

Runtime对象也无法直接创建,可以通过Runtime.getRuntime()来获取。与System类类似,Runtime类也提供了gc()runFinalization()方法;除此之外,Runtime类还提供了load(filename)loadLibrary(libname)方法来加载文件和动态链接库。

Runtime runtime = Runtime.getRuntime();
runtime.gc();
runtime.runFinalization();
runtime.load(filename);
runtime.loadLibrary(libname);

Runtime还可以单独启动一条进程来运行操作系统的命令:

Runtime runtime = Runtime.getRuntime();
try {
	//运行计算器
	runtime.exec("calc");
} catch (IOException e) {
	e.printStackTrace();
}

二、获取用户输入

1、Scanner

使用Scanner类可以很方便地获取用户的键盘输入,Scanner是一个基于正则表达式的文本扫描器,可以从文件、输入流、字符串中解析出基本类型值和字符串。

Scanner类主要使用以下方法判断或获取输入:

  • hasNext()hasNextXxx()

    是否有下一个输入项,例如:hasNextInt()hasNextBoolean()等,hasNext()方法判断是否包含下一个字符串。

  • next()nextXxx()

    获取下一个输入项,例如:next()nextFloat()nextBigDecimal等。

  • hasNextLine()

    是否还有下一行输入。

  • nextLine()

    获取下一行输入。

Scanner默认使用空白作为多个输入项之间的分隔符,可以使用useDelimiter(pattern)方法设置分隔符,参数是正则表达式。

  • 样例:获取控制台输入并打印

      Scanner scanner = new Scanner(System.in);
      scanner.useDelimiter("\n");
      while(scanner.hasNext()){
          System.out.println(scanner.next());
      }
    

    输入:how are you 输出:how are you

    如果不设置分隔符,即去掉:scanner.useDelimiter("\n");

    输入:how are you 输出:

      how
      are
      you
    
  • 样例:读取文件内容并输出

Scanner scanner = new Scanner(new File("G://abc.html"));
//按行读取文件内容
while(scanner.hasNextLine()){
	System.out.println(scanner.nextLine());
}

2、BufferedReader

可以用BufferedReader来包装System.in,然后使用它的readLine()方法来逐行读取键盘输入。BufferedReaderJava IO中的一个字符包装流,它必须建立在一个字符流的基础上,但System.in是字节流,因此需要使用转换流InputStreamReaderSystem.in转换为字符流。

Scanner类不同的是,BufferedReader不能读取基本类型输入项(Long、Boolean等),它只能读取字符串:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while((str = br.readLine()) != null){
	System.out.println(str);
}

三、字符串与数值

1、Object类

Object类是所有类的父类,所有Java对象都可以调用Object类的方法,常用方法:

  • equals()
public boolean equals(Object obj) {
	return (this == obj);
}
  • hashCode()
public native int hashCode();

返回对象的hashCode值,通常与System.identityHashCode(Object x)方法返回值相同。

  • getClass()
public final native Class<?> getClass();

返回对象运行时的类。

  • toString()
public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

返回对象的字符串表示:运行时类名@hashCode的十六进制值。

  • finalize()
protected void finalize() throws Throwable { }

当程序中该对象不再被引用时,垃圾回收器调用此方法清理该对象的资源。

2、字符串

  • String

String类是不可变的,当一个String对象被创建后,包含在这个字符串中的字符序列是不可改变的,直到此对象销毁。

例如:

String str = "hello" + "-" + "world";

上面的例子中,除了使用了3个字符串直接量之外,还会额外生成2个字符串常量:"hello-""hello-world";因为String是不可变的,所以会导致生成很多临时变量,而使用StringBufferStringBuilder就可以避免这个问题。

  • StringBuffer

StringBuffer代表可变的字符序列,当一个StringBuffer对象被创建后,可以使用append()reverse()等方法来改变字符序列。可以通过调用toString()方法转换为String对象。

  • StringBuilder

StringBuilderStringBuffer类似,只不过StringBuffer是线程安全的,而StringBuilder不是线程安全的,因此性能略高。

StringStringBuilderStringBuffer它们都有length属性,但String类的length属性是不可变的,而后两者的length属性是可变的。

3、数值

  • Math

Java提供了Math工具类来实现一些复杂的数学运算,该类中提供了大量静态方法和两个静态属性:Math.PI(π)和Math.E(e)。

System.out.println(Math.PI);//3.141592653589793
System.out.println(Math.E);//2.718281828459045
//乘方
System.out.println(Math.pow(2, 3));//8.0
//绝对值
System.out.println(Math.abs(-0.65));//0.65
//平方根
System.out.println(Math.hypot(3, 4));//5.0
//角度、弧度转化
System.out.println(Math.toDegrees(Math.toRadians(90)));//90.0
//三角函数
System.out.println(Math.sin(Math.toRadians(30)));//0.49999999999999994
//向下取整
System.out.println(Math.floor(1.2));//1.0
//向上取整
System.out.println(Math.ceil(1.2));//2.0
//四舍五入
System.out.println(Math.round(1.56));//2
//伪随机数(0≤x<1)
System.out.println(Math.random());
  • Random

    Random类专门用于生成一个伪随机数,它的构造器如下:

    • public Random()

      使用默认的种子

    • public Random(long seed)

      使用指定的种子

    它可以生成各种类型伪随机数:整数、浮点数或者指定随机数的范围,例如:

    • nextInt()

      生成一个int范围内的整数

    • nextInt(bound)

      生成一个0 ~ bound范围内的整数

    • nextBoolean()

      随机生成一个布尔类型值

    • nextFloat()

      随机生成一个0.0 ~ 1.0之间的浮点数

    ……

    如果使用相同的种子创建Random对象,再以相同的方法产生随机数,那么结果是一样的:

      Random random = new Random(555);
      System.out.println(random.nextInt(100));
      System.out.println(random.nextLong());
      System.out.println(random.nextBoolean());
    
      Random another = new Random(555);
      System.out.println(another.nextInt(100));
      System.out.println(another.nextLong());
      System.out.println(another.nextBoolean());
    

    程序输出:

      38
      1257936417059039565
      false
    
      38
      1257936417059039565
      false
    

    为了避免两个Random对象生成相的伪随机数,可以使用当前时间作为Random的种子:

      Random random = new Random(System.currentTimeMillis());
    
  • BigDecimal

floatdouble两种浮点类型在计算时很容易引起精度丢失,例如:

System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));
System.out.println("1.0 - 0.55 = " + (1.0 - 0.55));

程序输出:

0.1 + 0.2 = 0.30000000000000004
1.0 - 0.55 = 0.44999999999999996

Java提供了BigDecimal类来精确表示、计算浮点数,该类提供了很多构造器来创建BigDecimal对象。

使用BigDecimal(double val)构造器时仍有一定的不可预知性:

BigDecimal decimal = new BigDecimal(0.1);
System.out.println(decimal);

输出:

0.1000000000000000055511151231257827021181583404541015625

因此建议优先使用基于String的构造器:

BigDecimal decimal = new BigDecimal("0.1");
System.out.println(decimal);//0.1

如果必须使用double类型数构造BigDecimal对象时,应该通过BigDecimal.valueOf(double val)方法创建。

BigDecimal类提供了addmultiplypow等方法进行算术运算:

BigDecimal num = new BigDecimal("0.1");
System.out.println("0.1 + 0.2 = " + num.add(new BigDecimal("0.2"))); 
System.out.println("0.1 - 0.055 = " + num.subtract(new BigDecimal("0.055")));

num = new BigDecimal("1.0");
System.out.println("1.0 / 2.0 = " + num.divide(new BigDecimal("2.0")));
System.out.println("1.0 * 6.0 = " + num.multiply(new BigDecimal("6.0")));

输出:

0.1 + 0.2 = 0.3
0.1 - 0.055 = 0.045
1.0 / 2.0 = 0.5
1.0 * 6.0 = 6.00

四、日期与时区

1、日期

  • Date

    Java提供了java.util.Date类来处理日期和时间,常用构造器和方法如下:

    • Date()

      生成一个代表当前日期时间的Date对象,此方法调用Date(long date)构造器,所传参数为System.currentTimeMillis()

    • Date(long date)

      使用指定的long型整数生成Date对象。

    • getTime()

      返回Date对象对应的long型整数,即与GMT 1970-01-01 00:00:00的时间差。

    • after(Date when)、before(Date when)

      测试该日期是否在指定日期之后或之前。

      Date date = new Date();
      System.out.println(date.getTime());//1535881629374
      Date ago = new Date(1535881515741L);
      System.out.println(date.after(ago));//true
      System.out.println(date.before(ago));//false
    
  • Calendar

    Calendar是一个抽象类,用于表示日历。该类提了getInstance()方法来创建Calendar对象,在创建时也可以指定TimeZoneLocale

    CalendarDate之前可以互相转换:

      //从Calendar对象获得Date对象
      Calendar calendar = Calendar.getInstance();
      Date date = calendar.getTime();
    
      //根据Date对象创建对应的Calendar对象
      Date date = new Date();
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(date);
    

    Calendar类提供了很多获取、修改日期、时间的方法:

    • int get(int field)

      获取日历指定字段的值

    • void set(int field, int value)

      设置日历指定字段的值

    • void set(int year, int month, int date, int hourOfDay, int minute, int second);

      设置日历的年、月、日、时、分、秒

    • void add(int field, int amount)

      根据日历的规则,增加或减少指定字段的值。

    • void roll(int field, int amount)

      add()方法类似,此方法中如果日历字段添加amount后超过了该字段表示的最大范围后,不会向上一个字段进位。

    上面参数中的fieldCalendar类中的静态属性,例如:Calendar.MONTH

      Calendar calendar = Calendar.getInstance();
      //int get(int field)
      System.out.println("年份: " + calendar.get(Calendar.YEAR));
      //void set(year, month, date, hourOfDay, minute, second);
      calendar.set(2016, 5, 5, 15, 30, 45);
      //2016-6-5 15:30:45
      System.out.println(calendar.getTime());
      //年份减2
      calendar.add(Calendar.YEAR, -2);
      //2014-6-5 15:30:45
      System.out.println(calendar.getTime());
      //月份加8
      calendar.roll(Calendar.MONTH, 8);
      //2014-2-5 15:30:45
      System.out.println(calendar.getTime());
    

容错性设置

还可以通过setLenient(lenient)方法来设置Calendar的容错性,Calendar默认是支持容错性的,例如:设置某一字段的值使其超过该字段上限时会自动向上一字段进位;如果关闭容错性,则不允许设置某字段的值超过其上限。

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, 15);
System.out.println(calendar.get(Calendar.MONTH));

calendar.setLenient(false);
calendar.set(Calendar.MONTH, 15);
//下面获取Month值时报错
System.out.println(calendar.get(Calendar.MONTH));

程序输出:

3
Exception in thread "main" java.lang.IllegalArgumentException: MONTH
	at java.util.GregorianCalendar.computeTime(Unknown Source)
	at java.util.Calendar.updateTime(Unknown Source)
	at java.util.Calendar.complete(Unknown Source)
	at java.util.Calendar.get(Unknown Source)
	at com.strikeback.base.CalendarTest.main(CalendarTest.java:42)

set()方法的延迟修改

如上面的程序所示,在调用set(field, value)方法修改日历字段时,Calendar所代表的时间并不会立即修改,直到下次调用get()getTime()add()roll()等方法时才会重新计算日历的时间,这就是set方法的延迟修改,优势是多次调用set()方法并不会触发多次不必要的计算。

Calendar calendar = Calendar.getInstance();
calendar.set(2018, 7, 31);//2018-08-31
calendar.set(Calendar.MONTH, 8);
//set方法延迟修改,上一步并不会立即计算时间 [2018-10-01(9月没有31号)]
calendar.set(Calendar.DAY_OF_MONTH, 7);
System.out.println(calendar.getTime());//2018-09-07

2、时区

Java中使用TimeZone表示时区,TimeZone是一个抽象类,可以通过调用它的静态方法:getDefault()getTimeZone(id)来创建实例。

常用方法:

  • getDefault()

    获取机器默认的时区对象

  • getTimeZone(ID)

    获取指定的时区对象

  • getID()

    返回该时区的ID

  • getDisplayName()

    返回时区名称

  • getAvailableIDs()

    获取Java支持的所有时区ID

TimeZone timeZone = TimeZone.getDefault();
System.out.println(timeZone.getID());//Asia/Shanghai
System.out.println(timeZone.getDisplayName());//中国标准时间

String[] ids = TimeZone.getAvailableIDs();
System.out.println(Arrays.toString(ids));

timeZone = TimeZone.getTimeZone("America/Chicago");
System.out.println(timeZone.getDisplayName());//中央标准时间
参考资料:
  • 《疯狂Java》