前言
参考
- 黑马程序员
- BiliBili 遇见狂神说:https://space.bilibili.com/95256449/channel/index
Object
身为一切的祖宗,
Object
当然是不能不说的,到了以后,会经常使用Object
的方法
常用方法
clone()
深拷贝和浅拷贝
在讲解一切之前,首先要了解一下深拷贝和浅拷贝
拷贝,顾名思义就是复制。拷贝分为深拷贝和浅拷贝。这个概念其实并不是Java独有的。
假设现在有一个A,我们对A进行拷贝,得到B
这个时候,假如A发生了改变,而B也跟着改变,这就叫做浅拷贝。假设A发生了改变而B不改变,这就叫做深拷贝
clone()
在之前,我们对对象的创建通常使用
new
关键字。new
的意思是分配给这个对象一块和它类型对应的内存空间,并对这个对象进行初始化。现在我们如果想创建一个新的对象,有了一个新的方法:
clone()
。这个方法是存在于Object类中的,这意味着一切对象都可以使用这个方法。但是这个方法的访问修饰符是
protected
,这意味着我们要修改为public
使用这个方法一般需要两步骤
- 实现
Cloneable
,否则会爆出CloneNotSupportedException
- 重写
clone()
方法
我们来测试一下clone()
方法:
package com.howling;
public class CommonClass {
public static void main(String[] args) {
CloneClass clonedClass = new CloneClass("小明",18);
CloneClass cloneClass = null;
try {
cloneClass = clonedClass.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
clonedClass.setName("小刚");
clonedClass.setAge(20);
System.out.println(cloneClass.getName());//小明
System.out.println(cloneClass.getAge());//18
}
}
class CloneClass implements Cloneable{
private String name;
private int age;
public CloneClass(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public CloneClass clone() throws CloneNotSupportedException {
return (CloneClass) super.clone();
}
}
表面看上去仿佛没有任何问题,那么这究竟是深克隆还是浅克隆呢?只需要一步简单的验证即可
我们只需要查看在对象的使用上是否为一个对象就可以判定
- 假如两个对象的地址值相同,就是浅克隆,这意味着直接把对象的地址值引用过去的
- 假如两个对象的地址值不同,就是深克隆,这意味着直接复制了一份新的对象
package com.howling;
public class CommonClass {
public static void main(String[] args) {
CloneClass clonedClass = new CloneClass(new Student(18));
CloneClass cloneClass = null;
try {
cloneClass = clonedClass.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//com.howling.Student@1b6d3586
System.out.println(clonedClass.getStudent().toString());
//com.howling.Student@1b6d3586
System.out.println(cloneClass.getStudent().toString());
}
}
class CloneClass implements Cloneable{
private Student student;
public CloneClass(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public CloneClass clone() throws CloneNotSupportedException {
return (CloneClass) super.clone();
}
}
class Student{
private int age;
public Student(int age) {
this.age = age;
}
}
地址值相同,所以
clone
方法实现的是浅克隆
clone
方法仅仅只是拷贝了基本数据类型,而对于深层次的引用类型其实并没有作为一个真正的拷贝,真正存在的还是原来的地址值
toString()
作用
没有覆写的时候 | 覆写的时候 |
---|---|
返回对象的地址值 | 一般覆写的时候就是返回对象的具体内容 |
对象类型@内存地址 |
具体内容 |
com.howling.Student@1b6d3586 |
Student{age=18} |
一般重写的形式为:
@Override public String toString() { return "Student{" + "age=" + age + '}'; }
一般不是太垃圾的编辑器都会帮助我们进行重写,比如IDEA,比如远古的Eclipse
equals
作用
比较和其他对象是否相等的方法
默认的话,比较的是地址值,因此来比较两个对象是否相等
现在我们也会将这个方法重写,用来比较对象之间具体的内容
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age; }
==和equals的比较
两者的分析:
equals() |
== |
|
---|---|---|
基本类型 | 比较内容 | 比较内容 |
引用类型 | 比较地址(没有覆写的时候)/ 比较内容(覆写的时候) | 比较地址 |
再来看Object中,equals的源码:
public boolean equals(Object obj) { return (this == obj); }
可以看到,equals最终的本质其实也是
==
现在,系统类大部分类都覆写了
equals
这个方法
hashcode()
作用
返回对象的哈希值
Hash
Hash,在中文翻译中为散列,程序员一般直接叫音译哈希。
哈希是指把任意长度的输入通过一定的算法压缩成固定长度的输出(哈希值)的函数(算法)。哈希算法有多种。
也就是说,只要算法相同,输入相同,哈希值必定相同
而且,算法不同,输入不同,哈希值在理论上也有可能相同,只不过概率很低,因为哈希值是算出来的
而hashcode就是根据内存的地址值算出来的一个数值
hashcode的意义
假如我已经存放了一万个数,这时候我要存放一万零一个数,能想到最笨的方法就是挨个遍历一遍。
但是现在有了hashcode,我只需要找到对应的位置
- 假如这个位置没有数字,直接填写上
- 假如这个位置有数字,比对hashcode相同的数字就好了
Objects
compare
/*-------------------比较-------------------*/
/*
Objects.compare(参数1,参数2,Comparator比较方法)
执行的结果:-1
Comparator自定义
使用普通的值来进行演示
*/
int i = 0, i2 = 1;
System.out.println(Objects.compare(i, i2, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}));
/*-------------------------------------------------------------------*/
/*
一个函数式接口,可以使用lambda表达式进行简写
*/
int[] arr = {1, 2, 3, 4, 5};
int[] arr2 = {1, 2, 3, 4, 5};
System.out.println(Objects.compare(arr, arr2, (o1, o2) -> Arrays.equals(o1, o2) ? 1 : 0));//1
isNull
/*-------------------判断是否为null-------------------*/
System.out.println(Objects.isNull(123));//false
System.out.println(Objects.isNull(null));//true
equals
/*----------------------判断是否相同----------------------*/
System.out.println(Objects.equals(1, 1));//true
System.out.println(Objects.equals(1, 2));//false
System.out.println(Objects.equals("1", "1"));//true
System.out.println(Objects.equals(new Person(),new Person()));//false
class Person{}
String
构造方式
- 直接创建
- 使用
byte[]
创建 - 使用
char[]
创建
/*-------------------构造方法的比较-------------------*/
String str1 = new String("abcd");
String str2 = "abcd";
System.out.println("str:" + str1 + "--" + str2);//str:abcd--abcd
String strByte1 = new String(new byte[]{97, 98, 99, 100});
String strByte2 = new String(new byte[]{97, 98, 99, 100}, 0, 1);
System.out.println("strByte:" + strByte1 + "--" + strByte2);//strByte:abcd--a
String strChar1 = new String(new char[]{'a', 'b', 'c', 'd'});
String strChar2 = new String(new char[]{'a', 'b', 'c', 'd'}, 0, 1);
System.out.println("strChar:" + strChar1 + "--" + strChar2);//strChar:abcd--a
常用方法
查找
获取字符串长度
/*--------------------------长度--------------------------*/
String str = "1234567123456712345671234567";
System.out.println(str.length());//28
指定字符第一次/最后一次出现的位置
/*--------------------------指定字符第一次/最后一次出现的位置--------------------------*/
String str = "1234567123456712345671234567";
System.out.println(str.indexOf("1"));//0
System.out.println(str.indexOf("1", 1));//7
System.out.println(str.lastIndexOf("1"));//21
System.out.println(str.lastIndexOf("1", 7));//7
返回指定位置的字符
/*--------------------------指定字符第一次/最后一次出现的位置--------------------------*/
/*charAt:返回指定位置的字符*/
String str = "1234567123456712345671234567";
System.out.println(str.charAt(0));//1
断言
是否以指定字符开头/结尾
/*--------------------------确认是否以指定的字符串开头/结尾--------------------------*/
String str = "ABCABCABC";
/*
startsWith(String prefix):确认是否以指定的字符串开头
startsWith(String prefix, int toffset):确认在指定的所引处是否以指定的字符串开头
endsWith(String suffix):确认指定的字符串是否在指定的后缀结尾
*/
System.out.println(str.startsWith("ABC"));//true
System.out.println(str.startsWith("ABC", 1));//false
System.out.println(str.endsWith("BC"));//true
是否与其他字符串相等(忽略大小写/不忽略大小写)
/*--------------------------比较--------------------------*/
String str1 = "ABCABCABC";
String str2 = "abcabcabc";
/*
equals:比较两个字符串
equalsIgnoreCase:比较两个字符串,忽略大小写
*/
System.out.println(str1.equals(str2));//false
System.out.println(str1.equalsIgnoreCase(str2));//true
注意,使用
equals
和==
是不同的,前者比较值,后者比较地址
是否包含指定的字符串
/*--------------------------确认是否包含--------------------------*/
String str1 = "ABCABCABC";
String str2 = "abcabcabc";
/*
contains(CharSequence s)
CharSequence的所有已知实现类:
CharBuffer , Segment , String , StringBuffer , StringBuilder
*/
System.out.println(str1.contains("A"));//true
是否为空
/*--------------------------是否为空--------------------------*/
String str = "1234567123456712345671234567";
System.out.println(str.isEmpty());//false
是否遵从指定的正则表达式
/*--------------------------是否遵从指定的正则表达式--------------------------*/
String str = "1234567123456712345671234567";
System.out.println(str.matches(".*"));//true
替换
字符替换
/*--------------------------替换--------------------------*/
String str = "1234567123456712345671234567";
//*234567*234567*234567*234567
System.out.println(str.replace('1', '*'));
//-4567-4567-4567-4567
System.out.println(str.replaceAll("123", "-"));
//?4567123456712345671234567
System.out.println(str.replaceFirst("(123)", "?"));
字符串拼接
/*--------------------------连接--------------------------*/
String str1 = "ABCABCABC";
String str2 = "abcabcabc";
System.out.println(str1.concat(str2));//ABCABCABCabcabcabc
去除首尾空格
/*--------------------------去除首尾空格--------------------------*/
String str1 = " ABCABCABC ";
System.out.println(str1.trim());//ABCABCABC
分割
拆分(直接拆分/根据索引拆分)
String str = "ABCABCABC";
/*
split(String regex):根据正则直接拆分
split(String regex, int limit):根据正则拆分,并指定分成几份
*/
Arrays.stream(str.split("A")).forEach(i-> System.out.print(i+"-"));//-BC-BC-BC-
Arrays.stream(str.split("A",4)).forEach(i-> System.out.print(i+"-"));//-BC-BC-BC-
转换
转字符数组
/*--------------------------转换--------------------------*/
String str = "AbcaBCabC";
/*转为字符数组*/
for (char c : str.toCharArray()) {
System.out.print(c);//AbcaBCabC
}
转小写
/*--------------------------转换--------------------------*/
String str = "AbcaBCabC";
/*转小写*/
System.out.println(str.toLowerCase());//abcabcabc
转大写
/*--------------------------转换--------------------------*/
String str = "AbcaBCabC";
/*转大写*/
System.out.println(str.toUpperCase());//ABCABCABC
特性
字符串的不可变性和字符串常量池
在刚才比较字符串的时候我们已经说过,equals
比较值,==
比较地址,我们可以使用==
来做一个说明
第一题:
String s1 = "abc"; // 常量池
String s2 = new String("abc"); // 堆内存中
System.out.println(s1 == s2); // false两个对象的地址值不一样。
System.out.println(s1.equals(s2)); // true
第二题:
String s1 = "a" + "b" + "c";
String s2 = "abc";
System.out.println(s1 == s2);//true
System.out.println(s1.equals(s2));//true
第三题:
String s1="ab";
String s2="abc";
String s3=s1+"c";
System.out.println(s3==s2); // false
System.out.println(s3.equals(s2)); // true
以上三道题充分考到了字符串的底层。
现在我们来看一下字符串的创建方式:两种
String str = "str"; //第一种方式,进行字面量赋值
String str = new String("str"); //第二种方式,进行对象的创建
字符串常量池
我们都知道,字符串是不可变的,但是字符串又是需要大量使用的,这个时候考虑到时间和空间上的开销,我们果断的选择将一些字符串放入到一块对应的空间中,以后只要出现相同的字符串,就可以直接从这块空间中拿到。
这块空间就是字符串常量池。
那么再回头说我们创建对象的两种方式:
第一种方式
进行字面量赋值,这种方式会将之前没有创建过的字符串创建出来并且放入到字符串常量池中。
以后只要需要值相同的字符串,直接从字符串常量池中取得即可
String str = "abc";
String str1 = "abc";
System.out.println(str == str1);//true
第二种方式
第二种方式一定会进行对象的创建,但是在这种情况下有两种情况:
1、情况一:之前已经有字符串存在于字符串常量池中了,那么在堆中开辟一块内存空间A指向字符串常量池中的这个地址B,然后把A交上去
这种情况下有个问题,就是我们最终引用的地址确实是字符串常量池中的地址,但是因为我们最后交上去的其实是堆的地址,所以即使最终引用的是字符串常量池中的地址,和之前的地址也不可能相同!
String str1 = "abc";
String str = new String("abc");
System.out.println(str == str1);//false
2、情况二:之前没有字符串在字符串常量池中,这个时候直接在堆中开辟一块空间,但是不将数据放到常量池中
String str1 = new String("abc");
池化:强制字符串使用字符串常量池
上面我们说,当使用对象的方式创建一个字符串时,分为两种情况,字符串没有的时候会直接在堆中而不会放到字符串常量池中。
假如我们想要将这个字符串放入到字符串常量池中,需要使用池化技术。
池化技术也分为两种情况:
情况一
我们将对象方式创建的字符串进行池化,发现字符串常量池中没有这个字符串,那么会在字符串常量池中开辟一块空间,然后指向堆中的字符串
String str = new String("abc");
String str1 = str.intern();
String str2 = "abc";
System.out.println(str2 == str1);//true
情况二
我们将对象方式创建的字符串进行池化,发现字符串常量池中竟然存在这个字符串,那么就直接交出字符串常量池中的这个字符串。
String str = "abc";
String str1 = new String("abc");
String str2 = str1.intern();
System.out.println(str == str2);//true
字符串中的加号
字符串中的加号在本质上是使用了StringBuilder
的append
操作
我们来看下面一段代码:
String str1 = "ab";
String str2 = str1 + "c";
在这里,我们的str1肯定存在于字符串常量池中,但是str2却不是,因为它使用了加号的操作。
我们说,加号的本质其实是使用了StringBuilder的append操作,上面的str做了这样的操作:
1、从字符串常量池中取出ab
2、新建一个StringBuilder
3、进行StringBuilder的append操作
4、将StringBuilder转换为字符串的地址赋值给str2
所以,str2和str1的地址值肯定是不相同的,除非我们使用字符串的池化技术,强制将str2使用字符串常量池。
字符串加号的效率十分低下
加号的操作其实本质上是StringBuilder的append操作,但是有一个问题,那就是它只要使用一个加号就会创建并销毁一次StringBuilder,直到最后剩下的那个才会转换为字符串,然后将地址值交出去
我们来看下面一段代码
String c = "c";
String str = "a" + "b" + c + "d" + "e";
这段代码进行了如下操作:
1、在字符串常量池中增加了c字符串,并且赋予了变量c
2、str的操作
3、首先创建一个StringBuilder,是这样的:StringBuilder builder1 = new StringBuilder("ab")
我们可以看到,StringBuilder其实进行了一个优化,直接创建了变量之前的所有数据
4、builder1.append(c)
5、销毁builder1,创建builder2
6、builder2进行append操作,增加一个值,也就是d这个字符串(在变量之后,就会一个一个增加)
7、销毁builder2
....
最后剩下的builder转换了字符串,然后将地址交给了str
StringBuffer和StringBuilder
体系结构
- String:实现
Serializable
和CharSequence
- StringBuilder:实现
Serializable
和CharSequence
,继承AbstractStringBuilder
- StringBuffer:实现
Serializable
和CharSequence
,继承AbstractStringBuilder
构造方法
StringBuilder()
:构造一个没有字符的,初始容量为16个字符StringBuilder(int capacity)
:指定容量构建StringBuilder(String str)
:构建一个初始化为指定字符串的
两者差不多,以一个举例子
常用方法
添加
追加
StringBuilder builder = new StringBuilder();
/*追加字符串*/
builder.append("ab");
builder.append("bc");
builder.append("CD");
/*追加字符*/
builder.append('a');
/*追加字符数组*/
builder.append(new char[]{'a', 'b', 'c'});
/*指定字符数组的范围追加*/
builder.append(new char[]{'a', 'b', 'c'}, 0, 1);
/*追加不是字符串的其他常量,以字符串形式追加,事实上可以追加Object*/
builder.append(1);
builder.append(1.0);
builder.append(false);
builder.append(1L);
/*追加StringBuffer和StringBuilder*/
builder.append(new StringBuilder("...StringBuilder..."));
builder.append(new StringBuffer("...StringBuffer..."));
System.out.println(builder.toString());
/*abbcCDaabca11.0false1...StringBuilder......StringBuffer...*/
插入
StringBuilder builder = new StringBuilder();
builder.append("_|--|_");
/*
插入方法就是插队,原本的数据被顶到了后面去
insert有多个重载:
- insert(int offset, boolean b):指定位置插入,插入boolean类型的参数
- insert(int offset, char c):指定位置插入,插入char类型的参数
- insert(int offset, char[] str):指定位置插入,插入char数组
- insert(int index, char[] str, int offset, int len):指定位置插入,插入指定范围的数组
- 可以插入的类型还有int,double,float,object ....
*/
builder.insert(3,"0false1...StringBuilder......StringBuffer");
System.out.println(builder.toString());//_|-0false1...StringBuilder......StringBuffer-|_
查找
返回容量
StringBuilder builder = new StringBuilder();
builder.append("_|--|_");
builder.insert(3, "0false1...StringBuilder......StringBuffer");
/*返回容量*/
System.out.println(builder.capacity());//47
返回字符长度
StringBuilder builder = new StringBuilder();
builder.append("_|--|_");
builder.insert(3, "0false1...StringBuilder......StringBuffer");
/*返回字符长度*/
System.out.println(builder.length());//47
根据索引查询字符
StringBuilder builder = new StringBuilder();
builder.append("_|--|_");
builder.insert(3, "0false1...StringBuilder......StringBuffer");
/*查询指定索引处的字符*/
System.out.println(builder.charAt(10));//.
根据字符查询索引
StringBuilder builder = new StringBuilder();
builder.append("_|--|_");
builder.insert(3, "0false1...StringBuilder......StringBuffer");
/*查询字符首次出现的位置*/
System.out.println(builder.indexOf("."));//10
/*查询字符首次出现的位置,从指定的位置开始*/
System.out.println(builder.indexOf(".", 15));//26
/*查询字符最后一次出现的位置*/
System.out.println(builder.lastIndexOf("_"));//46
/*查询字符最后一次出现的位置,到指定的位置结束*/
System.out.println(builder.lastIndexOf("_", 2));//0
替换
替换
StringBuilder builder = new StringBuilder("1234567890");
/*替换指定范围的字符,使用指定的字符串代替*/
System.out.println(builder.replace(0, 4, "."));//.567890
/*指定位置,替换*/
builder.setCharAt(0,'_');
System.out.println(builder.toString());//_567890
删除
StringBuilder builder = new StringBuilder("1234567890");
/*----------------------------删除----------------------------*/
/*删除指定范围的字符*/
System.out.println(builder.delete(0, 3).toString());//4567890
/*删除指定位置的字符*/
System.out.println(builder.deleteCharAt(0).toString());//567890
删除其实也是另类的替换,你完全可以使用空字符串替换掉原有的
分割
分割
StringBuilder builder = new StringBuilder("123456789");
/*截取为String,从指定位置开始截取*/
System.out.println(builder.substring(0));//123456789
/*截取为String,指定范围截取,左闭右开,包括左边不包括右边*/
System.out.println(builder.substring(0, 1));//1
转换
转换为字符串
StringBuilder builder = new StringBuilder("0123456789");
System.out.println(builder.toString());//0123456789
翻转
StringBuilder builder = new StringBuilder("0123456789");
/*翻转*/
System.out.println(builder.reverse().toString());
特性
三者的联系
这三者指的是:
- String
- StringBuilder
- StringBuffer
String,StringBuilder和StringBuffer
在讲String
的时候我们曾经说过,有一个字符串常量池专门存放字符串的东西,但是字符串常量池其实还达不到我们的需求
因为字符串常量池底层是存放的字符串,只有当字符串相同的时候才可以复用,但是如果需要这个字符串的子字符串就不能复用
即使可以复用,也是复用的相同字符串,同样是创建的新的对象
于是StringBuilder和StringBuffer出现了
StringBuffer和StringBuilder的对象可以被多次修改而不产生新的对象,用于更好的复用
String
private final char value[];
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
final
的char[],不可变
StringBuilder
//构造1,直接创造一个容量为16的StringBuilder
public StringBuilder() {
super(16);
}
//构造2,创造一个容量为 字符串长度+16的 StringBuilder
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
//增加的方法,直接调用的父类方法
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
//父类的追加方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();//假如为空,返回
int len = str.length();
ensureCapacityInternal(count + len);
/*
使用的是System.arraycopy(上一个char[],0,value,count,len-0);
简单来讲就是原有的字符数组+新的字符数组形成一个字符数组
*/
str.getChars(0, len, value, count);
count += len;
return this;//返回
}
StringBuffer
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
@Override
public synchronized StringBuffer reverse() {
toStringCache = null;
super.reverse();
return this;
}
和StringBuilder高度相似,不过所有的方法都加上了
synchronized
,
StringBuilder和StringBuffer
StringBuilder
是线程不安全的,但是效率高,所以单线程下一般用这个StringBuffer
是线程安全的,但是效率相对StringBuilder
要低
这个我们在查看源码的时候就已经看到了,StringBuffer的所有方法全部都加上了
synchronized
Scanner
构造方法
Scanner:一个扫描器,可以通过不同源得到文本
@Test
public void Step1() throws IOException {
File file = new File("hello.txt");
InputStream inputStream = new FileInputStream(file);
//从指定的文件得到值
Scanner fileScanner = new Scanner(file);
//从输入流中得到值
Scanner streamScanner = new Scanner(inputStream);
//从指定的字符串的导致
Scanner strScanner = new Scanner("hello world");
//从系统输入得到值
Scanner scanner = new Scanner(System.in);
inputStream.close();
}
常用API
打印
@Test
public void Step2() throws FileNotFoundException {
File file = new File("src/hello.txt");
Scanner scanner = new Scanner(file);
/*
结果:
Hello
World
- scanner.hashNext():作用是判断是否存在下一个值,到空格和回车停止
- scanner.next():指针指向下一个值,也就是读取下一个值,到空格和回车停止,也就是不读取回车和空格
*/
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
/*
结果:Hello World
- scanner.hasNextLine():作用是判断是否存在下一个值,空格不停止回车停止
- scanner.nextLine():指针指向下一个值,也就是读取下一个值,可以读取空格,到回车停止
*/
while (scanner.hasNextLine()){
System.out.println(scanner.nextLine());
}
}
人机交互
@Test
public void Step3() {
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
int i = scanner.nextInt();
}
Scanner的nextXXX方法的作用就是读取控制台中输入的字符,其中字符串为
next
,其他为nextXXX
,比如nextInt
,nextDouble
等等
Random
伪随机和真随机
- 区分伪随机和真随机的只有一点:结果是否由算法得出来的
计算机使用的是算法得出来的,也就是说,计算是伪随机的
当我们调用产生随机数的方法时,计算机会抓出一些数据,比如当前时间,CPU的温度,风扇的转动等等,然后将这些数据投入到算法中,最后得出来最终的结果,产生最后给出你的这个数据。
也就是说,假如你可以完美的模拟出来当时的情景,计算机得到的数据就是确定的。
人脑得到的随机数是真随机的
我们想要得到一个随机数,就算你模拟出来所有的情景,也不见得人给你的一个随机数是那个确定的随机数
有了确定的输入,但是输出是不确定的,所以这个随机是真随机
构造方法和常用API
@Test
public void Step1() {
//构造方法
Random random = new Random();
/*
- random.nextInt():随机出一个int类型的数据
- random.nextInt(100):随机出一个在[0,100)之间的数据,左闭右开(不包括100)
- random.nextFloat():随机出一个float类型的数据
- random.nextDouble():随机出一个double类型的数据
- random.nextBoolean():随机出一个boolean类型的数据
- random.nextBytes(bytes):随机出byte类型的数据,并且充满到byte数组中,没有返回值
*/
int nextI = random.nextInt();
int nextIRandom = random.nextInt(100);
byte[] bytes = new byte[10];
random.nextBytes(bytes);
System.out.println(Arrays.toString(bytes));
}
Math
@Test
public void test1() {
//返回绝对值,返回double
int abs = Math.abs(-5);
//向下取整,返回double
double ceil = Math.ceil(-5.5);
//向上取整,返回double
double floor = Math.floor(-5.5);
//随机,返回double,随机返回0~1
double random = Math.random();
//四舍五入,返回long
long round = Math.round(5.5);
}
日期和时间
Date
构造方法
- Date():新建一个Date,时间是当前时间
- Date(long date):输入一个long类型,时间根据输入的来
- Date(year,month,day):输入年月日,时间按照输入来
常用API
Date date = new Date();
System.out.println("当前时间" + date.getTime());
System.out.println("当前年:" + date.getYear());
System.out.println("当前月" + date.getMonth());
事实上,Data类已经有非常多过时的方法了,现在了解即可,没有必要深究
SimpleDateFormat
配合Date使用
定义格式化日期的规则,首先来看一下格式化的规则:
字符 | 意义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
定义规则
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//将字符串解析为date对象
Date parse = format.parse("2020-7-28 15:17:20");
System.out.println(parse.getTime());
//将date解析为字符串
System.out.println(format.format(date));
Calendar
在Date之后出现,替换了非常多的Date方法,更近一层
构造
Calendar是抽象类,使用
getInstance()
进行创建,时间为默认的时区和语言环境来创建的
Calendar calendar = Calendar.getInstance();
常用API
@Test
public void test1() throws ParseException {
Calendar calendar = Calendar.getInstance();
//通过日历获得date
System.out.println(calendar.getTime());
//获得年
System.out.println(calendar.get(Calendar.YEAR));
//获得月,注意,这里的月是要+1才符合中国人的习惯,外国人是0~11
System.out.println(calendar.get(Calendar.MONTH) + 1);
//获得天,月中的天
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//获得天,周中的天
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));
//获得天:年中的天
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
//手动将时间进行改变,也可以设置月,天,等等
calendar.add(Calendar.YEAR, -3);
//可以直接设置
calendar.set(2019, 10, 11);
}
LocalDateTime
别再用之前的时间操作了,现在要使用新时代的时间操作:
LocalDateTime
,并且LocalDateTime是线程安全的
接下来的内容来自于程序羊:https://mp.weixin.qq.com/s/v-Va_GuSUGr9HVAW84kloQ
构造
//指定时间构造:2019-10-12 9:21:32
LocalDateTime time = LocalDateTime.of(2019, 10, 12, 9, 21, 32);
//获得当前时间
LocalDateTime now = LocalDateTime.now();
获得当前时间
//获得当前时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间" + now);
System.out.println("当前年份" + now.getYear());
//月份按照英文,比如JULY
System.out.println("当前月份" + now.getMonth());
System.out.println("当前日,按月" + now.getDayOfMonth());
System.out.println("当前日,按周" + now.getDayOfWeek());
System.out.println("当前日,按年" + now.getDayOfYear());
System.out.println("当前时" + now.getHour());
System.out.println("当前分" + now.getMinute());
System.out.println("当前秒" + now.getSecond());
时间加减
//减少1年
LocalDateTime minusYears = now.minusYears(1);
//增加一年
LocalDateTime plusYears = now.plusYears(1);
//直接设置
LocalDateTime withYear = now.withYear(2020);
格式化
//格式化:yyyy-MM-dd
String format1 = now.format(DateTimeFormatter.ISO_DATE);
System.out.println(format1);
//格式化:yyyyMMdd
String format2 = now.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(format2);
//格式化:自定义
String format3 = now.format(DateTimeFormatter.ofPattern("yyyy__MM__dd"));
System.out.println(format3);
时间反解析
//时间反解析
LocalDateTime parse = LocalDateTime.parse
("2020-12-21 11:22:31", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(parse);