目录
利用System.currentTimeMillis()方法计算程序执行时间
String 类
- String表示字符串的类型,属于引用数据类型,不属于基本数据类型
- 在Java中随便使用双引号括起来的都是String对象。例如:“abc”
- java中规定,双引号括起来的字符串,是不可变的
在jdk中双引号括起来的字符串,例如:“abc” 都是直接存储在方法区的“字符串常量池”中的。
为什么呢?因为字符串在实际开发中使用太频繁,为了保证效率,所以把字符串放到了方法区的字符串常量池中
例如
public class Test{
public static void main(String[] args) {
String s1 = "abcdef";
String s2 = "abcdef" + "xy";
//new出的对象都是在堆里
String s3 = new String("xy");
}
}
内存结构图如图所示
当没有用new 开新对象时,要判断两个String引用类型指向的字符串内容是否相同,可以直接用“==”判断。
例如
public class Test{
public static void main(String[] args) {
String s1 = "abcdef";
String s2 = "abcdef";
if(s1 == s2){
System.out.println("yes");
}else{
System.out.println("no");
}
}
}
因为,s1 和 s2 均指向字符串常量池中的"abcdef" 对象。
如果用new 的话,例如
public class Test{
public static void main(String[] args) {
String s1 = new String("abcdef");
String s2 = new String("abcdef");
if(s1 == s2){
System.out.println("yes");
}else{
System.out.println("no");
}
}
}
则会输出no,因为s1 和 s2是指向了堆中的两个不同的string对象,这两个对象引用了字符串常量池中的同一个"abcdef"。
因此s1和s2的值是不同的,应该使用equals方法比较。
面试题:下面程序一共创建了几个字符串对象
答案:3个
public class Test{
public static void main(String[] args) {
String s1 = new String("abcdef");
String s2 = new String("abcdef");
}
}
第一个是字符串常量池中的String对象,还有两个是在堆里的String对象
常用方法
1.char charAt(int index)
返回索引处的字符
2.int compareTo(String anotherString)
按照字典序比较两个字符串
3.boolean contains(CharSequence s)
判断字符串里是否包含s
4.boolean endsWith(String suffix)
判断当前字符串是否以某个字符串结尾
5.boolean startsWith(String prefix)
判断当前字符串是否以某个字符串开头
6.boolean equalsIgnoreCase(String str)
判断两个字符串是否相等,忽略大小写
7.byte[] getBytes()
返回当前字符串的byte数组
8.int indexOf(String str)
判断某个字符串在当前字符串中第一次出现处的索引值
9.int lastIndexOf(String str)
判断某个字符串在当前字符串中最后一次出现的索引
10.String replace(CharSequence target, CharSequence replacement);
将当前字符串中的某个字符串替换为另一个字符串
11.String[] split(String regex)
将当前字符串按照regex分割为多个字符串
12.String substring(int beginindex) 参数是起始下标
截取起始下标之后的字符串
13.String substring(int beginindex,int endindex) 参数是起始下标和截止下标
截取beginindex和endindex之间的字符串,endindex不包含
14.char [] toCharArray()
将字符串转换为char数组
15.toLowerCase()
转小写
16.toUpperCase()
转大写
17.String trim()
去除字符串前后的空格
18.静态方法 String.valueOf(参数)
将参数转换成字符串
如果是String.valueOf(new 类名) 则会自动调用这个类的toString方法
StringBuffer类
StringBuffer 类是可变字符串类,创建 StringBuffer 类的对象后可以随意修改字符串的内容。每个 StringBuffer 类的对象都能够存储指定容量的字符串,如果字符串的长度超过了 StringBuffer 类对象的容量,则该对象的容量会自动扩大。
StringBuffer 类提供了 3 个构造方法来创建一个字符串,如下所示:
- StringBuffer() 构造一个空的字符串缓冲区,并且初始化为 16 个字符的容量。
- StringBuffer(int length) 创建一个空的字符串缓冲区,并且初始化为指定长度 length 的容量。
- StringBuffer(String str) 创建一个字符串缓冲区,并将其内容初始化为指定的字符串内容 str,字符串缓冲区的初始容量为 16 加上字符串 str 的长度。
思考:实际开发中,如果需要进行字符串的拼盘拼接,会有什么问题?
因为java中的字符串是不可变的,每一次拼接都会产生新字符串,这样会占用大量方法区内存,造成内存空间的浪费。
例如
public class test{
public static void main(String[] args) {
String s1 = "a";
s1 += "b";
s1 += "c";
s1 += "d";
}
}
/*
首先String s1 = "a",会在字符串常量池中创建一个“a”的字符串对象
然后s1+="b" 此时s1 = “ab”,由于字符串常量池中没有"ab",所以它又会创建一个字符串对象
之后s1+="c" 和 s1+=“d” 均会创建一个字符串对象。于是字符串常量池中现在就有了四个对象,其中"ab","abc"均是中间产生了垃圾,占用了内存.
*/
String类之所以占用方法区内存是因为 String类底层使用final修饰的char数组实现的(java8),因此如果想进行拼接的话,就必须创建新的char数组因此创建新的字符串会占用方法区内存。原来存在的final修饰的char数组无法被释放
StringBuffer类就不一样了,它底层是用char数组实现的,所以StringBuffer类中的对象长度可变。
底层原理:StringBuffer类提供了append()方法来向当前字符串后添加字符串,每次添加会检查添加后字符串的长度是否超出了缓冲区的长度(实际上就是char数组的长度),如果超出了则创建一个更大的char数组,并进行数组的拷贝,拷贝完成后,原来的char数组就会被回收,并且由于char数组并没有被final修饰,所以它是保存在堆里的,是可以被释放的,并不会占用常量池中的内存。这样就节省了内存。
除此之外还有个StringBuilder类
使用StringBuilder也可以实现字符串的拼接,区别就是StringBuilder是非线程安全的,StringBuffer是线程安全的
八种基本包装类
java中为八种基本数据类型又对应准备了八种包装数据类型。他们是引用数据类型,父类是Object
这里有两个概念:拆箱和装箱
拆箱:将包装类转换为基本数据类型
装箱:将基本数据类型装欢为包装类
这是因为,有些时候方法的参数是Object引用类型,基本数据类型无法做参数,因此才有了八种包装数据类型
八种基本数据类型分别对应的包装类型
byte | Byte | 继承于Number |
short | Short | 继承于Number |
int | Integer | 继承于Number |
long | Long | 继承于Number |
float | Float | 继承于Number |
double | Double | 继承于Number |
boolean | Boolean | 继承于Object |
char | Character | 继承于Object |
Number类
Number类中有很多方法是用来将包装类转换成基本数据类型,例如
byte | byteValue() 返回指定号码作为值 |
abstract double | doubleValue() 返回指定数字的值为 |
abstract float | floatValue() 返回指定数字的值为 |
abstract int | intValue() 返回指定号码作为值 |
abstract long | longValue() 返回指定数字的值为 |
short | shortValue() 返回指定号码作为值 |
自动装箱与自动拆箱
装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
优点:基本类型和引用类型可以直接运算,
缺点:容易引发空指针异常
public class test{
public static void main(String[] args) {
//自动装箱
Integer x = 900;
//自动调用toString方法
System.out.println(x);
//自动拆箱
int y = x;
//x自动拆箱为int类型,注意只有+ - * / %可以自动拆箱,== 不可以
System.out.println(x+1);
}
}
java中为了提高程序的执行效率,将[-128,127]之间所有的包装对象提前创建好,放到了方法区的“整数型常量池”中,目的是只要用这个区间的数据就不需要再new了,直接从整数型常量池中取出来。
验证:
public class test{
public static void main(String[] args) {
Integer a = 128;
Integer b = 128;
System.out.println(a==b);
Integer x = 127;
Integer y = 127;
System.out.println(x==y);
}
}
输出结果为
内存图如下所示
但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。
总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。
下面我们进行一个归类:
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。
下面对Integer派别进行一个总结,如下图:
Integer类
常用方法
1.String转int
static int parseInt(String s)
String类型字符串转换为int,注意,s必须是一个文本数字
2.toString(int ,int 进制),将int整数转成指定的进制数
static String toString(Integer x, int radix)
//radix表示进制
3.Integer的两个常量
int的最大值 Integer.MAX_VALUE
int的最小值 Integer.MIN_VALUE
4.Integer的构造方法
Integer(String s)
s必须是一个文本数字,不能含有其他符号
Integer(int i)
String、int和Integer之间相互转换
日期类
java.util.Date中提供了一个Date类来处理日期。
注意,是java.utile.Date,而不是java.sql.Date包,导包时候可能会导错。
创建一个当前时间的Date对象
Date d = new Date()
创建一个指定时间的Date对象
使用带参数的构造方法Date(int year, int month, int day) ,可以构造指定日期的Date类对象,Date类中年份的参数应该是实际需要代表的年份减去1900,实际需要代表的月份减去1以后的值。
//创建一个代表2014年6月12号的Date对象
Date d1 = new Date(2014-1900, 6-1, 12); (注意参数的设置)
正确获得一个date对象所包含的信息
Date d2 = new Date(2014-1900, 6-1, 12);
//获得年份 (注意年份要加上1900,这样才是日期对象d2所代表的年份)
int year = d2.getYear() + 1900;
//获得月份 (注意月份要加1,这样才是日期对象d2所代表的月份)
int month = d2.getMonth() + 1;
//获得日期
int date = d2.getDate();
//获得小时
int hour = d2.getHours();//不设置默认为0
//获得分钟
int minute = d2.getMinutes();
//获得秒
int second = d2.getSeconds();
//获得星期 (注意:0代表星期日、1代表星期1、2代表星期2,其他的一次类推了)
int day = d2.getDay();
日期格式化
Date类的默认格式是这样的
Sat Sep 19 15:36:25 CST 2020
不太适合中国人看,我们可以给它格式化
需要使用SimpleDateFormat类
它可以实现按将日期按特定的格式转换为字符串
例如
import java.text.SimpleDateFormat;
import java.util.Date;
public class test{
public static void main(String[] args) {
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTimeStr = sdf.format(nowTime);
System.out.println(nowTimeStr);
}
}
输出为
日期字符串转Date类型
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class test{
public static void main(String[] args) throws ParseException {
String s = "2020-9-19 15:47:30";
//这里构造函数里的参数必须和字符串里的格式相同
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);
System.out.println(d);
}
}
利用System.currentTimeMillis()方法计算程序执行时间
System.currentTimeMillis()获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数
因此我们只需要在程序开始时取一次时间,结束时取一次时间即可计算出程序大体花费的时间
这里简单总结下System类的相关属性和方法
System.out out是System类的静态变量
System.out.println() println()方法不是System类的 而是PrintStream类的
System.gc() 建议启动垃圾回收器
System.currentTimeMillis() 获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数
System.exit(0) 退出JVM
数字格式化
java提供了一个类——DecimalFormat类用来格式化数字
数字格式
0 一个数字
# 一个数字,不包括 0
. 小数的分隔符的占位符
, 分组分隔符的占位符
; 分隔格式。
- 缺省负数前缀。
% 乘以 100 和作为百分比显示
? 乘以 1000 和作为千进制货币符显示;用货币符号代替;如果双写,用 国际货币符号代替。如果出现在一个模式中,用货币十进制分隔符代 替十进制分隔符。
X 前缀或后缀中使用的任何其它字符,用来引用前缀或后缀中的特殊字符。
例如
DecimalFormat df1 = new DecimalFormat("0.0");
DecimalFormat df2 = new DecimalFormat("#.#");
DecimalFormat df3 = new DecimalFormat("000.000");
DecimalFormat df4 = new DecimalFormat("###.###");
System.out.println(df1.format(12.34)); //12.3
System.out.println(df2.format(12.34)); //12.3
System.out.println(df3.format(12.34)); //012.340
System.out.println(df4.format(12.34)); //12.34
import java.text.DecimalFormat;
public class Test{
public static void main(String[] args){
double pi=3.1415927;//圆周率
//取一位整数
System.out.println(new DecimalFormat("0").format(pi));//3
//取一位整数和两位小数
System.out.println(new DecimalFormat("0.00").format(pi));//3.14
//取两位整数和三位小数,整数不足部分以0填补。
System.out.println(new DecimalFormat("00.000").format(pi));//03.142
//取所有整数部分
System.out.println(new DecimalFormat("#").format(pi));//3
//以百分比方式计数,并取两位小数
System.out.println(new DecimalFormat("#.##%").format(pi));//314.16%
long c=299792458;//光速
//显示为科学计数法,并取五位小数
System.out.println(new DecimalFormat("#.#####E0").format(c));//2.99792E8
//显示为两位整数的科学计数法,并取四位小数
System.out.println(new DecimalFormat("00.####E0").format(c));//29.9792E7
//每三位以逗号进行分隔。
System.out.println(new DecimalFormat(",###").format(c));//299,792,458
//将格式嵌入文本
System.out.println(new DecimalFormat("光速大小为每秒,###米").format(c)); //光速大小为每秒299,792,458米
}
}
DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。上面的例子包含了差不多所有的基本用法,如果你想了解更多,请参考 DecimalFormat 类的文档。
BigDecimal类
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。
BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
常用构造方法
BigDecimal(int) 创建一个具有参数所指定整数值的对象
BigDecimal(double) 创建一个具有参数所指定双精度值的对象
BigDecimal(long) 创建一个具有参数所指定长整数值的对象
BigDecimal(String) 创建一个具有参数所指定字符串表示的数值的对象
对于浮点数,优先使用BigDecimal(String) 构造函数
看下面这个例子
import java.math.BigDecimal;
public class test{
public static void main(String[] args) {
//创建两个大整数对象
BigDecimal v1 = new BigDecimal(0.1);
BigDecimal v2 = new BigDecimal("0.1");
System.out.println(v1);
System.out.println(v2);
}
}
结果为
原因分析:
1)参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。
常用方法
add(BigDecimal)
BigDecimal对象中的值相加,返回BigDecimal对象
subtract(BigDecimal)
BigDecimal对象中的值相减,返回BigDecimal对象
multiply(BigDecimal)
BigDecimal对象中的值相乘,返回BigDecimal对象
divide(BigDecimal)
BigDecimal对象中的值相除,返回BigDecimal对象
toString()
将BigDecimal对象中的值转换成字符串
doubleValue()
将BigDecimal对象中的值转换成双精度数
floatValue()
将BigDecimal对象中的值转换成单精度数
longValue()
将BigDecimal对象中的值转换成长整数
intValue()
将BigDecimal对象中的值转换成整数
BigDecimal大小比较
不能使用简单的> < = 来判断,而是要用compareTo方法去判断
BigDecimal常见异常
通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
解决方法:
divide方法设置精确的小数点,如:divide(xxxxx,2)
随机数
使用Random类创建随机数
import java.util.Random;
public class test{
public static void main(String[] args) {
Random r = new Random();
//随机产生一个int类型取值范围内的数字
int num1 = r.nextInt();
System.out.println(num1);
//创建一个[0~101)的随机数
int num2 = r.nextInt(101);
System.out.println(num2);
}
}