文章目录
装箱拆箱
封装类-Number类
封装类:
所有的基本类型,都有对应的类类型
比如int
对应的类是Integer
这种类就叫做封装类
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//把一个基本类型的变量,转换为Integer对象
Integer it = new Integer(i);
//把一个Integer对象,转换为一个基本类型的int
int i2 = it.intValue();
}
}
Number类:
数字封装类有Byte,Short,Integer,Long,Float,Double
这些类都是抽象类Number
的子类
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//Integer是Number的子类,所以打印true
System.out.println(it instanceof Number);
}
}
基本类型转封装类-封装类转基本类型
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
}
}
自动装箱-自动拆箱
不需要调用构造方法,通过=
符号自动把 基本类型 转换为 类类型 就叫装箱
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//自动转换就叫装箱
Integer it2 = i;
}
}
不需要调用Integer
的intValue
方法,通过=
就自动转换成int
类型,就叫拆箱
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
//自动转换就叫拆箱
int i3 = it;
}
}
int的最大值可以通过其对应的封装类Integer.MAX_VALUE
获取
那到底是使用new呢,还是使用valueOf方法呢。new每次都会创建一个新的对象,而除了Float和Double 外的其他包装类,都会缓存包装类对象,减少需要创建对象的此数,节省空间提升性能。实际上,从java 9开始,这些构造方法已经被标记为过时了,推荐使用valueOf方法。
false
true //这里因为是int,与基本类型比较时,都是比较的值
true
Integer a = 10;
Integer b = 10;
a==b 也是true,因为 在-128-127内的数,是直接用的缓冲区的数(IntegerCache)
但是:
Integer a = 1000;
Integer b = 1000;
a==b 是false,不在缓冲区的数还需要再new
字符串转换
数字转字符串(ValueOf/toString)
方法1: 使用String
类的静态方法valueOf
方法2: 先把基本类型装箱为对象,然后调用对象的toString
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//方法1
String str = String.valueOf(i );
//方法2
Integer it = i;
String str2 = it.toString();
}
}
字符串转数字(Integer.parseInt(str))
除了Character外,
1、每一个包装类都有一个静态的valueOf(String)方法,根据字符串表示返回包装类对象
Boolean b = Boolean.valueOf("true");
Float f = Float.valueOf("132.23f");
2、也都有一个parseXXX(String)方法,根据字符串表示返回基本类型。
调用Integer
的静态方法parseInt
int i= Integer.parseInt("123");
boolean b = Boolean.parseBoolean("true");
但是输入的字符串必须和所有的方法对应。
String str3 = "3.1a4";
float i3 = Float.parseFloat(str3);
//异常 System.out.println(i3);//java.lang.NumberFormatException
对于整数类型,字符串表示除了默认的十进制外,还可以表示为其他进制,如二进制,八进制,十六进制,包装类有静态方法进行
相互转换。
System.out.println(Integer.toBinaryString(1234));//输出二进制
System.out.println(Integer.toHexString(1234));//输出十六进制
System.out.println(Integer.parseInt("3039",16));//按十六进制解析
'输出:'
11000000111001
3039
12345
常用常量
包装类中除了定义静态方法和实例方法外,还定义了一些静态变量。
如,TRUE,数值类型的MIN_VALUE,MAX_VALUE,无穷大等、
public static final boolean TRUE= new Boolean(true);
public static final int MIN_VALUE= 0x80000000;
public static final int MAX_VALUE= 0x7fffffff;
public static final double POSITIVE_INFINITY= 1.0/0.0;//正无穷大
public static final double NEGATIVE_INFINITY= -1.0/0.0;//负无穷大
public static final double NaN= 0.0d/0.0;//非数值
外部可以调用:
int b = Integer.MIN_VALUE;
数学方法:
java.lang.Math
提供了一些常用的数学运算方法,并且都是以静态方法的形式存在
四舍五入, 随机数,开方,次方,π,自然常数
package digit;
public class TestNumber {
public static void main(String[] args) {
float f1 = 5.4f;
float f2 = 5.5f;
//5.4四舍五入即5
System.out.println(Math.round(f1));
//5.5四舍五入即6
System.out.println(Math.round(f2));
//得到一个0-1之间的随机浮点数(取不到1)
System.out.println(Math.random());
//得到一个0-10之间的随机整数 (取不到10)
System.out.println((int)( Math.random()*10));
//开方
System.out.println(Math.sqrt(9));
//次方(2的4次方)
System.out.println(Math.pow(2,4));
//π
System.out.println(Math.PI);
//自然常数
System.out.println(Math.E);
}
}
格式化输出
如果不使用格式化输出,就需要进行字符串连接
,如果变量比较多,拼接就会显得繁琐。使用格式化输出,就可以简洁明了
%s 表示字符串
%d 表示数字
%n 表示换行
package digit;
public class TestNumber {
public static void main(String[] args) {
String name ="盖伦";
int kill = 8;
String title="超神";
//直接使用+进行字符串连接,编码感觉会比较繁琐,并且维护性差,易读性差
String sentence = name+ " 在进行了连续 " + kill + " 次击杀后,获得了 " + title +" 的称号";
System.out.println(sentence);
//使用格式化输出
//%s表示字符串,%d表示数字,%n表示换行
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
System.out.printf(sentenceFormat,name,kill,title);
}
}
printf和format
printf和format
能够达到一模一样的效果,如何通过eclipse查看java源代码 可以看到,在printf
中直接调用了format
package digit;
public class TestNumber {
public static void main(String[] args) {
String name ="盖伦";
int kill = 8;
String title="超神";
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
//使用printf格式化输出
System.out.printf(sentenceFormat,name,kill,title);
//使用format格式化输出
System.out.format(sentenceFormat,name,kill,title);
}
}
换行符
换行符就是另起一行 — ‘\n’ 换行(newline)
回车符就是回到一行的开头 — ‘\r’ 回车(return)
在eclipse里敲一个回车,实际上是回车换行符
Java是跨平台的编程语言,同样的代码,可以在不同的平台使用,比如Windows,Linux,Mac
然而在不同的操作系统,换行符是不一样的
(1)在DOS和Windows中,每行结尾是 “\r\n”
;
(2)Linux系统里,每行结尾只有 “\n”
;
(3)Mac系统里,每行结尾是只有 "\r"
。
为了使得同一个java
程序的换行符在所有的操作系统中都有一样的表现,使用%n
,就可以做到平台无关的换行
System.out.printf("这是换行符%n");
System.out.printf("这是换行符%n");
字符
保存一个字符的时候使用char
package character;
public class TestChar {
public static void main(String[] args) {
char c1 = 'a';
char c2 = '1';//字符1,而非数字1
char c3 = '中';//汉字字符
char c4 = 'ab'; //只能放一个字符
}
}
char对应的封装类: Character
public static void main(String[] args) {
char c1 = 'a';
char c2 = '3';
Character c = c1; //自动装箱
c1 = c;//自动拆箱
Character常见方法
package character;
public class TestChar {
public static void main(String[] args) {
System.out.println(Character.isLetter('a'));//判断是否为字母
System.out.println(Character.isLetter(c3));//判断是否为字母
System.out.println(Character.isDigit('a')); //判断是否为数字
System.out.println(Character.isWhitespace(' ')); //是否是空白
System.out.println(Character.isUpperCase('a')); //是否是大写
System.out.println(Character.isLowerCase('a')); //是否是小写
System.out.println(Character.isUpperCase(c1)&&Character.isLowerCase(c2));//逻辑使用
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
String a = 'a'; //不能够直接把一个字符转换成字符串
String a2 = Character.toString('a'); //转换为字符串
}
}
常见转义:
package character;
public class TestChar {
public static void main(String[] args) {
System.out.println("使用空格无法达到对齐的效果");
System.out.println("abc def");
System.out.println("ab def");
System.out.println("a def");
System.out.println("使用\\t制表符可以达到对齐的效果");
System.out.println("abc\tdef");
System.out.println("ab\tdef");
System.out.println("a\tdef");
System.out.println("一个\\t制表符长度是8");
System.out.println("12345678def");
System.out.println("换行符 \\n");
System.out.println("abc\ndef");
System.out.println("单引号 \\'");
System.out.println("abc\'def");
System.out.println("双引号 \\\"");
System.out.println("abc\"def");
System.out.println("反斜杠本身 \\");
System.out.println("abc\\def");
}
}
字符串
创建字符串
字符串即字符的组合,在Java中,字符串是一个类,所以我们见到的字符串都是对象
常见创建字符串手段:1.
每当有一个字面值出现的时候,虚拟机就会创建一个字符串2.
调用String的构造方法创建一个字符串对象3.
通过+
加号进行字符串拼接也会创建新的字符串对象
package character;
public class TestString {
public static void main(String[] args) {
String garen ="盖伦"; //字面值,虚拟机碰到字面值就会创建一个字符串对象
String teemo = new String("提莫"); //创建了两个字符串对象
char[] cs = new char[]{'崔','斯','特'};
String hero = new String(cs);// 通过字符数组创建一个字符串对象
String hero3 = garen + teemo;// 通过+加号进行字符串拼接
}
}
final
String
被修饰为final
,所以是不能被继承的
package character;
public class TestString {
public static void main(String[] args) {
MyString str = new MyString();
}
/*这里会报错,因为String不能被继承*/
static class MyString extends String{
}
}
immutable
immutable 是指不可改变的
比如创建了一个字符串对象
String garen ="盖伦";
不可改变的具体含义是指:
不能增加长度
不能减少长度
不能插入字符
不能删除字符
不能修改字符
一旦创建好这个字符串,里面的内容 永远 不能改变
String
的表现就像是一个常量
String garen ="盖伦";
字符串格式化
如果不使用字符串格式化,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐
字符串长度
length
方法返回当前字符串的长度
可以有长度为0的字符串,即空字符串
String name ="盖伦";
System.out.println(name.length());
'数字和字符之间可以通过互相转换'
char c = 'A';
short s = (short) c;
操纵字符串
- charAt(int index)获取指定位置的字符
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
char c = sentence.charAt(0);
- toCharArray() 获取对应的字符数组
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
char[] cs = sentence.toCharArray(); //获取对应的字符数组
System.out.println(sentence.length() == cs.length);
- subString截取子字符串
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//截取从第3个开始的字符串 (基0)
String subString1 = sentence.substring(3);
System.out.println(subString1);
//截取从第3个开始的字符串 (基0)
//到5-1的位置的字符串
//左闭右开
String subString2 = sentence.substring(3,5);
System.out.println(subString2);
- split 根据分隔符进行分隔
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//根据,进行分割,得到3个子字符串
String subSentences[] = sentence.split(",");
for (String sub : subSentences) {
System.out.println(sub);
}
//以字母作为分隔符
String yi = "asdgjhfsafgsjfgsjafgalsdjkgfalfgwierwihsdjkfhsjk";
for (String each : yi.split("s")) {
System.out.println(each);
}
- trim 去掉首尾空格
String sentence = " 盖伦,在进行了连续8次击杀后,获得了 超神 的称号 ";
System.out.println(sentence);
//去掉首尾空格
System.out.println(sentence.trim());
- toLowerCase 全部变成小写 toUpperCase 全部变成大写
String sentence = "Garen";
//全部变成小写
System.out.println(sentence.toLowerCase());
//全部变成大写
System.out.println(sentence.toUpperCase());
- indexOf 判断字符或者子字符串出现的位置 contains 是否包含子字符串
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
System.out.println(sentence.indexOf('8')); //字符第一次出现的位置
System.out.println(sentence.indexOf("超神")); //字符串第一次出现的位置
System.out.println(sentence.lastIndexOf("了")); //字符串最后出现的位置
System.out.println(sentence.indexOf(',',5)); //从位置5开始,出现的第一次,的位置
System.out.println(sentence.contains("击杀")); //是否包含字符串"击杀"
- replaceAll 替换所有的 replaceFirst 只替换第一个
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
String temp = sentence.replaceAll("击杀", "被击杀"); //替换所有的
temp = temp.replaceAll("超神", "超鬼");
System.out.println(temp);
temp = sentence.replaceFirst(",","");//只替换第一个
System.out.println(temp);
比较字符串:
- 是否是同一个对象
str1
和str2
的内容一定是一样的!
但是,并不是同一个字符串对象
String str1 = "the light";
String str2 = new String(str1);
//==用于判断是否是同一个字符串对象
System.out.println( str1 == str2)
- 是否是同一个对象-特例**
str1 = "the light";
str3 = "the light";
一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
所以在第6行会创建了一个新的字符串"the light"
但是在第7行,编译器发现已经存在现成的"the light",那么就直接拿来使用,而没有进行重复创建
- 内容是否相同
使用equals进行字符串内容的比较,必须大小写一致
equalsIgnoreCase,忽略大小写判断内容是否一致
String str1 = "the light";
String str2 = new String(str1);
String str3 = str1.toUpperCase();
//==用于判断是否是同一个字符串对象
System.out.println( str1 == str2);
System.out.println(str1.equals(str2));//完全一样返回true
System.out.println(str1.equals(str3));//大小写不一样,返回false
System.out.println(str1.equalsIgnoreCase(str3));//忽略大小写的比较,返回true
- 是否以子字符串开始或者结束
startsWith //以…开始
endsWith //以…结束
String str1 = "the light";
String start = "the";
String end = "Ight";
System.out.println(str1.startsWith(start));//以...开始
System.out.println(str1.endsWith(end));//以...结束
StringBuffer
StringBuffer是可变长的字符串
StringBuffer常用方法:
StringBuffer sf=new StringBuffer();
// 用于字符串拼接
sf.append("abc");
sf.append(12);
System.out.println(sf);
// 删除指定位置的内容 [start,end)
sf.delete(3,5);
System.out.println(sf);
// 把[start,end)位置替换为str
sf.replace(1,3,"go");
System.out.println(sf);
// 在指定位置插入数据
sf.insert(2,"xxx");
System.out.println(sf);
// 截取[start,end)字符串
System.out.println(sf.substring(1,5));
// 返回字符串长度
System.out.println(sf.length());
// 获取索引为1的字符
System.out.println(sf.charAt(1));
// 将索引为1c处的值替换为'm'
sf.setCharAt(1,'m');
System.out.println(sf);
// 将字符串翻转
System.out.println(sf.reverse());
追加 删除 插入 反转
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "let there ";
StringBuffer sb = new StringBuffer(str1); //根据str1创建一个StringBuffer对象
sb.append("be light"); //在最后追加
System.out.println(sb);
sb.delete(4, 10);//删除4-10之间的字符
System.out.println(sb);
sb.insert(4, "there ");//在4这个位置插入 there
System.out.println(sb);
sb.reverse(); //反转
System.out.println(sb);
}
}
长度 容量
为什么StringBuffer
可以变长?
和String
内部是一个字符数组一样,StringBuffer
也维护了一个字符数组。 但是,这个字符数组,留有冗余长度
比如说new StringBuffer("the")
,其内部的字符数组的长度,是19
,而不是3
,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19
,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了 参考MyStringBuffer
length:
“the”的长度 3
capacity:
分配的总空间 19
注: 19这个数量,不同的JDK数量是不一样的
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the";
StringBuffer sb = new StringBuffer(str1);
System.out.println(sb.length()); //内容长度
System.out.println(sb.capacity());//总空间
}
}
String与StringBuffer的性能区别?
生成10位长度的随机字符串
然后,先使用String的+,连接10000个随机字符串,计算消耗的时间
然后,再使用StringBuffer连接10000个随机字符串,计算消耗的时间
提示: 使用System.currentTimeMillis()
获取当前时间(毫秒)
补充一下:事实上,从JDK1.5后,用加号拼接字符串几乎没有什么性能损失了。
还有另外要说明的(摘自网上):
最早是没有 stringbuilder 的,sun 的人不知处于何种考虑,决定让 stringbuffer 是线程安全的,然后大约 10 年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这 10 年之中这个愚蠢的决定为 java 运行速度慢这样的流言贡献了多大的力量,于是,在 jdk1.5 的时候,终于决定提供一个非线程安全的 stringbuffer 实现,并命名为 stringbuilder。顺便,javac 好像大概也是从这个版本开始,把所有用加号连接的 string 运算都隐式的改写成 stringbuilder,也就是说,从 jdk1.5 开始,用加号拼接字符串已经没有任何性能损失了。
上面说的"用加号拼接字符串已经没有任何性能损失了"并不严谨,严格的说,如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java 编译器会隐式的替换成 stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用 stringbuilder 吧。
MyStringBuffer
根据接口IStringBuffer
,自己做一个MyStringBuffer
IStringBuffer
接口:
package character;
public interface IStringBuffer {
public void append(String str); //追加字符串
public void append(char c); //追加字符
public void insert(int pos,char b); //指定位置插入字符
public void insert(int pos,String b); //指定位置插入字符串
public void delete(int start); //从开始位置删除剩下的
public void delete(int start,int end); //从开始位置删除结束位置-1
public void reverse(); //反转
public int length(); //返回长度
}
value和capacity
value:
用于存放字符数组
capacity:
容量
无参构造方法: 根据容量初始化value
public MyStringBuffer(){
value = new char[capacity];
}
package character;
public class MyStringBuffer implements IStringBuffer{
int capacity = 16;
int length = 0;
char[] value;
public MyStringBuffer(){
value = new char[capacity];
}
@Override
public void append(String str) {
// TODO Auto-generated method stub
}
@Override
public void append(char c) {
// TODO Auto-generated method stub
}
@Override
public void insert(int pos, char b) {
// TODO Auto-generated method stub
}
@Override
public void delete(int start) {
// TODO Auto-generated method stub
}
@Override
public void delete(int start, int end) {
// TODO Auto-generated method stub
}
@Override
public void reverse() {
// TODO Auto-generated method stub
}
@Override
public int length() {
// TODO Auto-generated method stub
return 0;
}
}
带参构造方法
//有参构造方法
public MyStringBuffer(String str){
if(null!=str)
value =str.toCharArray();
length = value.length;
if(capacity<value.length)
capacity = value.length*2;
}
反转reverse
@Override
public void reverse() {
for (int i = 0; i < length / 2; i++) {
char temp = value[i];
value[i] = value[length - i - 1];
value[length - i - 1] = temp;
}
}
插入insert 和 append
边界条件判断
插入之前,首先要判断的是一些边界条件。 比如插入位置是否合法,插入的字符串是否为空
- 扩容
- 要判断是否需要扩容。 如果插入的字符串加上已经存在的内容的总长度超过了容量,那么就需要扩容。
- 数组的长度是固定的,不能改变的,数组本身不支持扩容。 我们使用变通的方式来解决这个问题。
- 根据需要插入的字符串的长度和已经存在的内容的长度,计算出一个新的容量。 然后根据这个容量,创建一个新的数组,接着把原来的数组的内容,复制到这个新的数组中来。并且让value这个引用,指向新的数组,从而达到扩容的效果。
- 插入字符串
- 找到要插入字符串的位置,从这个位置开始,把原数据看成两段,把后半段向后挪动一个距离,这个距离刚好是插入字符串的长度
- 然后把要插入的数据,插入这个挪出来的,刚刚好的位置里。
- 修改
length
的值
最后修改length
的值,是原来的值加上插入字符串的长度 insert(int, char)
参数是字符的insert方法,通过调用insert(int, String)
也就实现了。append
追加,就是在最后位置插入。 所以不需要单独开发方法,直接调用insert
方法,就能达到最后位置插入的效果
@Override
public void insert(int pos, String b) {
//边界条件判断
if(pos<0)
return;
if(pos>length)
return;
if(null==b)
return;
//扩容
while(length+b.length()>capacity){
capacity = (int) ((length+b.length())*1.5f);
char[] newValue = new char[capacity];
System.arraycopy(value, 0, newValue, 0, length);
value = newValue;
}
char[] cs = b.toCharArray();
//先把已经存在的数据往后移
System.arraycopy(value, pos, value,pos+ cs.length, length-pos);
//把要插入的数据插入到指定位置
System.arraycopy(cs, 0, value, pos, cs.length);
length = length+cs.length;
}
删除 delete
package character;
public class MyStringBuffer implements IStringBuffer{
int capacity = 16;
int length = 0;
char[] value;
public MyStringBuffer(){
value = new char[capacity];
}
//有参构造方法
public MyStringBuffer(String str){
this();
if(null==str)
return;
if(capacity<str.length()){
capacity = value.length*2;
value=new char[capacity];
}
if(capacity>=str.length())
System.arraycopy(str.toCharArray(), 0, value, 0, str.length());
length = str.length();
}
@Override
public void append(String str) {
insert(length,str);
}
@Override
public void append(char c) {
append(String.valueOf(c));
}
@Override
public void insert(int pos, char b) {
insert(pos,String.valueOf(b));
}
@Override
public void delete(int start) {
delete(start,length);
}
@Override
public void delete(int start, int end) {
//边界条件判断
if(start<0)
return;
if(start>length)
return;
if(end<0)
return;
if(end>length)
return;
if(start>=end)
return;
System.arraycopy(value, end, value, start, length- end);
length-=end-start;
}
@Override
public void reverse() {
for (int i = 0; i < length/2; i++) {
char temp = value[i];
value[i] = value[length-i-1];
value[length-i-1] = temp;
}
}
@Override
public int length() {
// TODO Auto-generated method stub
return length;
}
@Override
public void insert(int pos, String b) {
//边界条件判断
if(pos<0)
return;
if(pos>length)
return;
if(null==b)
return;
//扩容
while(length+b.length()>capacity){
capacity = (int) ((length+b.length())*1.5f);
char[] newValue = new char[capacity];
System.arraycopy(value, 0, newValue, 0, length);
value = newValue;
}
char[] cs = b.toCharArray();
//先把已经存在的数据往后移
System.arraycopy(value, pos, value,pos+ cs.length, length-pos);
//把要插入的数据插入到指定位置
System.arraycopy(cs, 0, value, pos, cs.length);
length = length+cs.length;
}
public String toString(){
char[] realValue = new char[length];
System.arraycopy(value, 0, realValue, 0, length);
return new String(realValue);
}
public static void main(String[] args) {
MyStringBuffer sb = new MyStringBuffer("there light");
System.out.println(sb);
sb.insert(0, "let ");
System.out.println(sb);
sb.insert(10, "be ");
System.out.println(sb);
sb.insert(0, "God Say:");
System.out.println(sb);
sb.append("!");
System.out.println(sb);
sb.append('?');
System.out.println(sb);
sb.reverse();
System.out.println(sb);
sb.reverse();
System.out.println(sb);
sb.delete(0,4);
System.out.println(sb);
sb.delete(4);
System.out.println(sb);
}
}