1.String类
1).什么是字符串?
字符串是由多个字符组成的一串数据(字符序列),字符串可以看成是字符数组.
2).String类的概述
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
3).字符串常量池
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。
4).String的常见构造方法
public String():空构造,初始化一个新创建的 String 对象,使其表示一个空字符序列
public String(byte[] bytes):把字节数组转成字符串
public String(byte[] bytes,int index,int length):把字节数组的一部分转成字符串(index:表示的是从第几个索引开始, length表示的是长度)
public String(char[] value):把字符数组转成字符串
public String(char[] value,int index,int count):把字符数组的一部分转成字符串
public String(String original):把字符串常量值转成字符串
5).String类常用方法
public boolean equals(Object obj): 比较字符串的内容是否相同,区分大小写
public boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
public boolean contains(String str): 判断字符串中是否包含传递进来的字符串
public boolean startsWith(String str): 判断字符串是否以传递进来的字符串开头
public boolean endsWith(String str): 判断字符串是否以传递进来的字符串结尾
public boolean isEmpty(): 判断字符串的内容是否为空。
public int length(): 获取字符串的长度。
public char charAt(int index): 获取指定索引位置的字符
public int indexOf(int ch): 返回指定字符在此字符串中第一次出现处的索引。
public int indexOf(String str): 返回指定字符串在此字符串中第一次出现处的索引。
public int indexOf(int ch,int fromIndex): 返回指定字符在此字符串中从指定位置后第一次出现处的索引。
public int indexOf(String str,int fromIndex): 返回指定字符串在此字符串中从指定位置后第一次出现处的索引。
public String substring(int start): 从指定位置开始截取字符串,默认到末尾。
public String substring(int start,int end): 从指定位置开始到指定位置结束截取字符串。
public String replace(char old,char new) 将指定字符进行替换
public String replace(String old,String new) 将指定字符串进行替换
public String trim() 去除两端空格
注意:“String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
5).String深度理解
例1:
/**
* 采用字面值的方式赋值
*/
public void test1(){
String str1="aaa";
String str2="aaa";
System.out.println("===========test1============");
System.out.println(str1==str2);//true 可以看出str1跟str2是指向同一个对象
}
分析:当执行String str1="aaa"时,JVM首先会去字符串池中查找是否存在"aaa"这个对象,如果不存在,则在字符串池中创建"aaa"这个对象,然后将池中"aaa"这个对象的引用地址返回给字符串常量str1,这样str1会指向池中"aaa"这个字符串对象;如果存在,则不创建任何对象,直接将池中"aaa"这个对象的地址返回,赋给字符串常量。当创建字符串对象str2时,字符串池中已经存在"aaa"这个对象,直接把对象"aaa"的引用地址返回给str2,这样str2指向了池中"aaa"这个对象,也就是说str1和str2指向了同一个对象,因此语句System.out.println(str1 == str2)输出:true。
例2:
/**
* 采用new关键字新建一个字符串对象
*/
public void test2(){
String str3=new String("aaa");
String str4=new String("aaa");
System.out.println("===========test2============");
System.out.println(str3==str4);//false 可以看出用new的方式是生成不同的对象
}
分析: 采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"aaa"这个字符串对象,如果有,则不在池中再去创建"aaa"这个对象了,直接在堆中创建一个"aaa"字符串对象,然后将堆中的这个"aaa"对象的地址返回赋给引用str3,这样,str3就指向了堆中创建的这个"aaa"字符串对象;如果没有,则首先在字符串池中创建一个"aaa"字符串对象,然后再在堆中创建一个"aaa"字符串对象,然后将堆中这个"aaa"字符串对象的地址返回赋给str3引用,这样,str3指向了堆中创建的这个"aaa"字符串对象。当执行String str4=new String(“aaa”)时, 因为采用new关键字创建对象时,每次new出来的都是一个新的对象,也即是说引用str3和str4指向的是两个不同的对象,因此语句System.out.println(str3 == str4)输出:false。
例3:
/**
* 编译期确定
*/
public void test3(){
String s0="helloworld";
String s1="helloworld";
String s2="hello"+"world";
System.out.println("===========test3============");
System.out.println(s0==s1); //true 可以看出s0跟s1是指向同一个对象
System.out.println(s0==s2); //true 可以看出s0跟s2是指向同一个对象
}
分析:因为例子中的s0和s1中的"helloworld”都是字符串常量,它们在编译期就被确定了,所以s0s1为true;而"hello”和"world”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"helloworld”的一个引用。所以我们得出s0s1==s2。
例4:
/**
* 编译期无法确定
*/
public void test4(){
String s0="helloworld";
String s1=new String("helloworld");
String s2="hello" + new String("world");
System.out.println("===========test4============");
System.out.println( s0==s1 ); //false
System.out.println( s0==s2 ); //false
System.out.println( s1==s2 ); //false
}
分析:用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。s0还是常量池中"helloworld”的引用,s1因为无法在编译期确定,所以是运行时创建的新对象"helloworld”的引用,s2因为有后半部分new String(”world”)所以也无法在编译期确定,所以也是一个新创建对象"helloworld”的引用。
例5:
/**
* 继续-编译期无法确定
*/
public void test5(){
String str1="abc";
String str2="def";
String str3=str1+str2;
System.out.println("===========test5============");
System.out.println(str3=="abcdef"); //false
}
分析:因为str3指向堆中的"abcdef"对象,而"abcdef"是字符串池中的对象,所以结果为false。JVM对String str=“abc"对象放在常量池中是在编译时做的,而String str3=str1+str2是在运行时刻才能知道的。new对象也是在运行时才做的。而这段代码总共创建了5个对象,字符串池中两个、堆中三个。+运算符会在堆中建立来两个String对象,这两个对象的值分别是"abc"和"def”,也就是说从字符串池中复制这两个值,然后在堆中创建两个对象,然后再建立对象str3,然后将"abcdef"的堆地址赋给str3。
例6:
/**
* 编译期优化
*/
public void test6(){
String s0 = "a1";
String s1 = "a" + 1;
System.out.println("===========test6============");
System.out.println((s0 == s1)); //result = true
String s2 = "atrue";
String s3= "a" + "true";
System.out.println((s2 == s3)); //result = true
String s4 = "a3.4";
String s5 = "a" + 3.4;
System.out.println((s4 == s5)); //result = true
}
分析:在程序编译期,JVM就将常量字符串的"+“连接优化为连接后的值,拿"a” + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。
例7:
/**
* 编译期无法确定
*/
public void test7(){
String s0 = "ab";
String s1 = "b";
String s2 = "a" + s1;
System.out.println("===========test7============");
System.out.println((s0 == s2)); //result = false
}
分析:JVM对于字符串引用,由于在字符串的"+“连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a” + s1无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给s2。所以上面程序的结果也就为false。
例8:
/**
* 比较字符串常量的"+"和字符串引用的"+"的区别
*/
public void test8(){
String test="javalanguagespecification";
String str="java";
String str1="language";
String str2="specification";
System.out.println("===========test8============");
System.out.println(test == "java" + "language" + "specification");//true
System.out.println(test == str + str1 + str2);//false
}
分析:为什么出现上面的结果呢?这是因为,字符串字面量拼接操作是在Java编译器编译期间就执行了,也就是说编译器编译时,直接把"java"、“language"和"specification"这三个字面量进行”+“操作得到一个"javalanguagespecification” 常量,并且直接将这个常量放入字符串池中,这样做实际上是一种优化,将3个字面量合成一个,避免了创建多余的字符串对象。而字符串引用的"+“运算是在Java运行期间执行的,即str + str2 + str3在程序执行期间才会进行计算,它会在堆内存中重新创建一个拼接后的字符串对象。总结来说就是:字面量”+“拼接是在编译期间进行的,拼接后的字符串存放在字符串池中;而字符串引用的”+“拼接运算实在运行时进行的,新创建的字符串存放在堆中。对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I”+“love”+“java”; 的字符串相加,在编译期间便被优化成了"Ilovejava"。对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。
案例1:记录一个字符在字符串中出现的次数
//输入一个字符串,并再输入一个字符,输出该字符出现的次数
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
System.out.println("输入1:");
String str1=s.nextLine();
System.out.println("输入2:");
char str2=s.nextLine().charAt(0);
char[] chars = str1.toCharArray();
int count=0;
for(char c:chars){
if(c==str2){
count++;
}
}
System.out.println("字符"+str2+"出现了"+count+"次");
}
案例2:记录一个子串在整串中出现的次数
//输入一个字符串,并再输入一个字符串,输出该字符串出现的次数
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
System.out.println("输入1:");
String str1=s.nextLine();
System.out.println("输入2:");
String str2=s.nextLine();
int count =0;
find(str1,str2,count);
}
public static void find(String str1,String str2,int count){
int index=str1.indexOf(str2);
if(index>=0){
count++;
//如果能在总字符串里找到子字符串
//就进行截取字符串,从查到索引位置起数子字符串长度个开始截取,到最后结束
String news = str1.substring(index + str2.length(), str1.length());
find(news,str2,count);
}else {
System.out.println(count);
}
}
2.StringBuffer类
1).StringBuffer类概述
StringBuffer是线程安全的可变字符序列.我们如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间。而StringBuffer就可以解决这个问题.
2).StringBuffer构造方法
public StringBuffer(): 无参构造方法,构建一个不带字符的字符串缓冲区,大小为16个字符
public StringBuffer(int capacity): 指定容量的字符串缓冲区对象
public StringBuffer(String str): 指定字符串内容的字符串缓冲区对象
3).StringBuffer常用方法
public int capacity(): 返回当前容量的理论值
public int length(): 返回长度(字符数)。 实际值
public StringBuffer append(String str): 可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
public StringBuffer insert(int offset,String str): 在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
public StringBuffer deleteCharAt(int index): 删除指定位置的字符并返回本身
public StringBuffer delete(int start,int end): 删除从指定位置开始指定位置结束的内容,并返回本身
public StringBuffer replace(int start,int end,String str): 从start开始到end用str替换
public StringBuffer reverse(): 字符串反转
public String substring(int start): 从指定位置截取到末尾(返回值类型不再是StringBuffer本身)
public String substring(int start,int end): 截取从指定位置开始到结束位置,包括开始位置,不包括结束位置(返回值类型不再是StringBuffer本身)
4).String和StringBuffer互相转化
A::String --> StringBuffer
a:通过构造方法
b:通过append()方法
B:StringBuffer – > String
a: 使用substring方法
b:通过构造方法
c:通过toString()方法
3.StringBuilder
与StringBuffer操作一致,效率更快,但是线程不安全,
4.包装类
1).什么是包装类?
Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,为了使用方便和解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八种基本数据类型对应的类统称为包装类(Wrapper Class),包装类均位于java.lang包。
2).基本数据的包装类
3).包装类的实际使用
①基本数据和包装类的转换
int n=5;
Integer n1=new Integer(n);
System.out.println("int类型转换为integer类:"+n1);
Integer i=new Integer(50);
int i1 = i.intValue();
System.out.println("integer类转换为int类型:"+i1);
②数值字符串和整型互转
//parseInt方法: 数字字符串类型转成int类型
String ss="123";
int ii = Integer.parseInt(ss);
System.out.println("字符类型转成整型:"+ii);
//toString方法:int类型转成数字字符串类型
int ii2=123;
String ss2 = Integer.toString(ii2);
System.out.println("int类型转成数字字符串类型:"+ss);
4).拆箱与装箱
基本数据类型和对应的包装类可以相互转换,由基本数据类型向包装类转换称为装箱,反之为拆箱!
5.Date类与Calendar类
1).Date类
使用Date类的无参数构造方法创建的对象可以获取本地当前时间。
Date date=new Date();
时间戳:指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。
获取时间戳:
date.getTime();
System.currentTimeMillis();
SimpleDateFormat(格式化日期):日期和时间格式由 日期和时间模式字符串 指定。在 日期和时间模式字符串 中,未加引号的字母 ‘A’ 到 ‘Z’ 和 ‘a’ 到 ‘z’ 被解释为模式字母,用来表示日期或时间字符串元素。
也就是说,这些A——Z,a——z这些字母(不被单引号包围的)会被特殊处理替换为对应的日期时间,其他的字符串还是原样输出。
日期和时间模式:
yyyy:年
MM:月
dd:日
hh:1~12小时制(1-12)
HH:24小时制(0-23)
mm:分
ss:秒
S:毫秒
E:星期几
D:一年中的第几天
a:上下午标识
日期类转字符串:
Date ss = new Date();
System.out.println("一般日期输出:" + ss);
System.out.println("时间戳:" + ss.getTime());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//将目标格式传入构造方法
String time = format.format(ss.getTime());
字符串转日期类:
String s = "2019-03-15";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = format.parse(s);
2).Calendar类
在实际项目当中,我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年。。。。某些网站会记录下用户登陆的时间,比如银行的一些网站,仅需要获取当前时间的某部分,比如年份月份邓,
对于这些经常需要处理的问题,Java中提供了Calendar这个专门用于对日期进行操作的类(Date类在jdk1.1中也行,但后来都被淘汰掉了)。
首先我们来看Calendar的声明:
public abstract class Calendar extends Object implements Serializable, Cloneable, Comparable
该类被abstract所修饰,说明不能通过new的方式来获得实例,对此,Calendar提供了一个类方法getInstance,以获得此类型的一个通用的对象,getInstance方法返回一个Calendar对象(该对象为Calendar的子类对象),
其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
获取时间:
// 获取年
int year = calendar.get(Calendar.YEAR);
// 获取月,这里需要需要月份的范围为0~11,因此获取月份的时候需要+1才是当前月份值
int month = calendar.get(Calendar.MONTH) + 1;
// 获取日
int day = calendar.get(Calendar.DAY_OF_MONTH);
// 获取时
int hour = calendar.get(Calendar.HOUR);
// int hour = calendar.get(Calendar.HOUR_OF_DAY);// 24小时表示
// 获取分
int minute = calendar.get(Calendar.MINUTE);
// 获取秒
int second = calendar.get(Calendar.SECOND);
设置时间:
// 时
calendar.set(Calendar.HOUR_OF_DAY, 0);
// 分
calendar.set(Calendar.MINUTE, 0);
// 秒
calendar.set(Calendar.SECOND, 0);
// 毫秒
calendar.set(Calendar.MILLISECOND, 0);
Calendar和Data转化:
//Calendar转化为Data
Calendar calendar = Calendar.getInstance();//日历类的实例化
calendar.set(year, month - 1, day);//设置日历时间,月份必须减一
Date date = calendar.getTime(); // 从一个 Calendar 对象中获取 Date 对象
//Data转化为Calendar
Date date = new Date();//直接new对象,获取的是当前时间
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
案例3:算算自己活了多少天
public static void main(String[] args) {
//算算自己活了多少天
Date date=new Date();
long now=date.getTime();//现在的时间戳
Calendar c=Calendar.getInstance();
c.set(2000,2,30);//设置出生时间
Date date1=c.getTime();//获取出生时间戳
long birth=date1.getTime();
System.out.println((now-birth)/1000/60/60/24);
}
案例4:求下个月的今天的倒数3天
public static void main(String[] args) {
Calendar c=Calendar.getInstance();
c.set(Calendar.MONTH,c.get(Calendar.MONTH)+1);
c.set(Calendar.DAY_OF_MONTH,c.get(Calendar.DAY_OF_MONTH-3));
}
6.Math类
Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
1).Math类中常用方法
Math类中所有方法都为静态方法.
Math.floor(): 向下取整
Math.round(): 四舍五入
Math.ceil(): 取不小于num的最小整数
Math.abs(): 取绝对值
Math.random(): 生成[0,1)之间的随机小数
Math.pow(a,b): a的b次方
Math.sqrt(num): num的平方根
Math.max(): 返回参数中的最大值
随机生成[a,b]中的随机整数:(int)(Math.random()*(b-a+1)+a);
2).Math类中的成员变量
Math类中所有成员变量都是静态成员变量.
Math.E: 自然对数的底数e
Math.PI: 圆周率π
案例5:猜数字游戏
//随机生成[1,100]一个数字,由键盘输入数字进行猜测,如果猜测小了则提示小了,猜测7次不中,提示笨蛋并程序结束
public static void main(String[] args) {
int v = (int) (Math.random() * 100) + 1;
int count = 0;
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入:");
int a = sc.nextInt();
if(a>v){
System.out.println("大了");
}else if(a<v){
System.out.println("小了");
}else{
System.out.println("恭喜");
break;
}
count++;
if(count>=7){
System.out.println("笨蛋");
break;
}
}
}
7.Scanner类
1).Scanner概述
用于获取用户的键盘输入.
2).Scanner常用方法
hasNextXxx(): 判断下一个是否是某种类型的元素,其中Xxx可以是Int,Double等。
public int nextInt(): 获取一个int类型的值
public String nextLine(): 获取一个String类型的值
import java.util.Scanner;
public class Demo01_Scanner {
public static void main(String[] args) {
//2. 创建键盘录入数据的对象
Scanner sc = new Scanner(System.in);
//3. 接收数据
System.out.println("请录入一个整数:");
int i = sc.nextInt();
//4. 输出数据
System.out.println("i:"+i);
}
}
8.System类
1).概述
System 类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作。
2).常用方法
currentTimeMillis() :返回以毫秒为单位的当前时间(时间戳)–获取当前系统时间与1970年01月01日00:00点之间的毫秒差值;
import java.util.Date;
public class SystemDemo {
public static void main(String[] args) {
//获取当前时间毫秒值
System.out.println(System.currentTimeMillis()); // 1516090531144
//计算程序运行时间
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println("共耗时毫秒:" + (end ‐ start));
}
}
arraycopy(Object src, int srcPos, Object dest, int destPos, int length) :将数组中指定的数据拷贝到另一个数组中
5个参数含义为:
import java.util.Arrays;
public class Demo11SystemArrayCopy {
public static void main(String[] args) {
int[] src = new int[]{1,2,3,4,5};
int[] dest = new int[]{6,7,8,9,10};
System.arraycopy( src, 0, dest, 0, 3);
/*代码运行后:两个数组中的元素发生了变化
src数组元素[1,2,3,4,5]
dest数组元素[1,2,3,9,10]
*/
}
}