常用类
包装类
定义
1.针对八种基本数据类型相应的引用类型--包装类
2.有了类的特点就可以调用类中的方法
| 基本数据类型 | 包装类 |
|---|---|
| boolean | Boolean |
| char | Charater |
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
其中,除了布尔类型和字符类型,其余的父类都是Number



包装类和基本数据类型的转换
基本类型-->包装类型 装箱
包装类型-->基本类型 拆箱
package javaSEStudy.wrapper;
//演示 int <--> Integer 的装箱和拆箱
public class Integer_ {
public static void main(String[] args) {
//jdk5之前 手动拆箱与装箱
int num1 = 100;
// Integer integer = new Integer(num1);
// ps:该写法在jdk9已被废弃
// 或
Integer in = Integer.valueOf(num1);
//手动拆箱
int i1 = in.intValue();
System.out.println("====================================");
//在jdk5以后,就可以自动装箱和自动拆箱
int num2 = 200;
//自动装箱
//这种写法底层调用的是 Integer.valueOf(num2)
Integer i2 =num2;
//自动拆箱
//这总写法底层调用intValue()
int num3 = i2;
}
}
常见面试题
其他的数据类型基本类似,这里就不一一举例
注意:三元运算符是是一个整体
Object obj ? new Integer(1):new Double(2.0);
sout(obj);
上面会输出1.0
因为三元运算符是一个整体,其中double精度最高,所以输出double格式
包装类型和String类型的相互转换
package javaSEStudy.wrapper;
// 包装类型和String类型的相互转换
public class String_ {
public static void main(String[] args) {
/**
* 将Integer转换为String
*/
Integer num1 = 100;
// 方式一 ,注意该方法只是把一个新的对象交给了str1,而num1是没有变的
// 此例子中得到的是"100",而原来的num1没变,他们是互不相干的两个类型
String str1 = num1 + "";
// 方式二
String str2 = num1.toString();
// 方式三
String str3 = String.valueOf(num1);
/**
* 将String转换为Integer
*/
String str = "这是一个测试用例";
// 方式一 使用自动装箱
// 此方法前面会输出int类型,因为parseInt源码返回类型就是int,手动改为Integer,他会自动装箱,该写法没有问题
Integer i1 = Integer.parseInt(str);
// 方式二 使用构造器
// 该写法已被废弃,这里只做了解
// Integer i2 = new Integer(str);
}
}
Integer和Charater常用方法
Integer.MIN_VALUE // 返回最小值
Integer.MAX_VALUE // 返回最大值
Charater.isDigit('a') // 判断是不是数字
Charater.isLetter('a') // 判断是不是字母
Charater.isUpperCase('a') // 判断是不是大写
Charater.isLowerCase('a') // 判断是不是小写
Charater.isWhitespace('a') // 判断是不是空格
Charater.toUpperCase('a') // 转成大写
Charater.toLowerCase('A') // 转成小写
常见面试题

//This method will always cache values in the range -128 to 127,
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
讲解:
顺序输出 False True False
第一个输出假,是因为这里用的是new,分别new 了一个对象,这里是==.不同对象的hashCode不同,这里也就为False
第二个输出为真,需要看源码,源码如图所示,这里使用的是自动装箱,也就是采用的Integer.valuesOf,而在源码中,如果在-128~127这个范围内,则会返回这个值,否则返回一个new的对象
第三个则由源码可知,不在该范围内,会new 一个新的对象,所以他们不等,输出假
但是在这里要注意

这里两个都输出的是True
因为只要有基本数据类型参与比较的,则他们比较的就是值的大小
String类
String 类的理解和创建对象
- String 对象用于保存字符串,也就是一组字符序列
- 字符串常量对象是用双括号括起的字符序列
- 字符串的字符使用的是Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
- String类常用构造方法
- String s1 = new String();
- String s2 = new String(String original);
- String s3 = new String(char[] a);
- String s4 = new String(char[] a,int startIndex,int count)

这里String分别实现了三个接口
Serializable --> 如果实现了该接口,代表我们的对象可以串行化,一旦可以串行化,就代表String对象可以网络传输
Comparable --> 如果实现该接口,就说明String对象可以进行比较大小
ps:
5.String 是final类,不能被其他类继承
6.private final char[] value;这是String的源码,在底层被转换为字符数组存储
7.一定要注意:value是一个final类型,赋值后就 不可修改(内容可以修改,但是value指向的地址不可以修改)
package javaSEStudy.wrapper.String_;
public class String1 {
public static void main(String[] args) {
// 1.String 对象用于保存字符串,也就是一组字符序列
// 2.在这里"蔡徐坤"就是字符串常量 双括号引起来的字符序列
// 3.String类有很多的构造器,构造器的重载
// 4.两个重要接口Serializable(对象串行化,可进行网络传输) Comparable(字符串比较大小)
// 5.String 是final类,不能被其他类继承
// 6.private final byte[] value;这是String的源码,在底层被转换为字节数组存储
// 7.一定要注意:value是一个final类型,赋值后就 不可修改(内容可以修改,但是value指向的地址不可以修改)
String name = "蔡徐坤";
// 下面这种写法并没有报错,证明内容是可以修改的
final char[] value = {'a', 'b', 'c', 'd', 'e', 'f'};
value[0] = 'H';
// 下面这种写法就会报错,因为上面为final,无法赋值
// char[] a = {'a','c','e'};
// value = a; 不可以修改value的地址
}
}
创建String对象的两种方式
一 直接赋值: String str = "蔡徐坤";
二 调用构造器: String str = new String("蔡徐坤");
两种创建String对象的区别
一:先从常量池查看是否有"蔡徐坤"的数据空间,如果有则直接指向,如果没有则直接创建,然后指向;str最终指向的是常量池的空间地址;
二:先在堆中创建空间,里面维护了value属性,指向了常量池的蔡徐坤空间.如果常量池没有"蔡徐坤",直接创建,如果有,通过value指向,最终指向的是堆中的空间地址

练习题
第一问


所以最终输出结果为:T F T F
第二问

最终输出结果为:T T T F
第一个创建了两个对象p1和p2,然后他们指向的常量池是同一个,所以第一个sout使用equals比较字符串的内容是否相等,输出T
第二个使用的是p1.name和p2.name,比较的是常量池中的对象,他们指向的是同一个常量,所以相等
第三个这里的"hspedu"就是常量池中的"hspedu",所以他们的相等
第四个比较的是s1 和 s2,他们分别是堆中的两个对象,所以这里不等
字符串的特性
String是一个final类,代表不可变的字符序列
字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的
第一问

这里它先创建了一个对象s1,然后指向了常量池"hello",然后s1这个对象去查找常量池是否有"haha",没有它创建了一个"haha",然后指向了"haha",原来 s1 指向的 "hello" 对象依然存在于字符串常量池中(没有被销毁)
第二问

这里只创建了一个对象,编译器会在编译时对其优化,会将这两个字符串进行拼接为helloabc,然后去常量池寻找是否有该字符串.
第三问

总共创建了三个对象
package javaSEStudy.wrapper.String_;
public class String2 {
public static void main(String[] args) {
String a = "hello";
String b = "abc";
// 1.先创建一个 StringBuilder sb = StringBuilder();
// 2.执行 sb.append("hello");
// 3.再执行 sb.append("abc");
// 4.String c = sb.toString()
// 最后,其实c指向的是堆中的对象 value[],它是String类型,然后value指向池中的"helloabc"
String c = a + b;
String d = "helloabc";
System.out.println(c == d); // 输出F
}
}

第四问

package javaSEStudy.wrapper.String_;
public class String3 {
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.println(ex.ch);
}
}
class Test1 {
String str = new String("hsp");
final char[] ch = {'j', 'a', 'v', 'a'};
public void change(String str, char[] ch) {
str = "java";
ch[0] = 'h';
}
}
输出结果

分析:
我们先画出内存分析图(有点丑)

这里需要注意char数组存储在堆中,并且我们调用了一个方法会产生一个新栈
首先是堆中的一个空间会产生str和ch两个对象,str通过堆中产生一个value空间,在通过value指向池中的hsp,ch则直接指向堆中的空间,并创建一个数组存储java
然后我们main线程产生了一个对象ex,去指向堆中的两个对象
然后我们调用了一个方法,就产生了一个新的栈,他这里的两个对象是方法中的String str和char[] ch,注意这里str直接指向了堆中的value,再指向hsp,ch则直接指向了堆中的java数组,调用方法的时候,str发现常量池中没有java,它就创建了一个常量java,并且直接指向了常量池,不会再通过堆中value去指向该常量,而ch则直接修改了数组中的值
注意:此时方法结束,他的生命周期也到此为止,该栈会直接被销毁,回到main方法,在这里的str指向并未被修改,还是通过value指向的hsp,而ch的指向已被修改
最终,它输出的就是 hsp and hava
String类的常用方法
String类是保存字符串常量的,每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了StringBuilder和StringBuffer来增强String功能
常见方法一:
- equals :区分大小写判断内容是否相等
- equalsIgnoreCase :忽略大小写的判断内容是否相等
- length :获取字符的个数,字符串的长度
- indexOf :获取字符在字符串中第一次出现的索引,索引从0开始,如果找不到,则返回-1
- lastIndexOf :获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到则返回-1
- substring :截取指定范围的子串
- trim :去除前后空格
- charAt :获取某索引的处的字符,注意不能使用Str[index]这种方式
substring解释:
String name = "hello,张三";
sout(name.substring(6)); //该写法是指将字符串从索引6的位置,把后面所有的内容截取
sout(name.substring(0,5)); //表示从索引0开始截取,截取到第5个字符,左闭右开区间
charAt解释:
String str = "hello";
//这里我们想取出h
str[0] //该写法报错,String不是数组,他用的value存储,无法取出
str.charAt(0) //该写法才最终输出h
常见方法二:
- toUpperCase :将字符串全部转换为大写
- toLowerCase :将字符串全部转换为小写
- concat :拼接
- replace :替换字符串中的字符
- split :分割字符串,对于某些分割字符,我们需要转义,比如| \ 等
- compareTo :比较两个字符串大小,如果前者大,返回正数,如果后者大,返回负数,相等则返回零
- toCharArray :转换成字符数组
- startsWith :检查字符串是否以指定前缀开头
- formart :格式字符串,%s字符串,%c字符,%d整型,%.2f浮点型 这里%.2f保留小数点后两位,并且进行四舍五入操作
concat解释:
String s1 = "贾宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
sout(s1);
//最终输出结果 贾宝玉林黛玉薛宝钗together
replace解释:
注意此处,只是s1.replace()返回的结果将其替换,s1本身是不会发生改变,如果下面不用s1接收,改为s11接收返回结果,输出s1则会将原句输出
String s1 = "贾宝玉 and 林黛玉 林黛玉 林黛玉";
//将所有的林黛玉替换为薛宝钗
s1 = s1.replace("林黛玉","薛宝钗");
sout(s1);
//最终输出结果为 贾宝玉 and 薛宝钗 薛宝钗 薛宝钗
split解释:
String[] poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
// 以逗号为标准,对 poem 进行分割,返回一个数组
String[] split = poem.split(",");
for(i<0;i<splint.length;i++){
sout(split[i]);
}
// 在对字符串进行分割时,如果有特殊字符,需要加入转义符 \
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
for(i<0;i<splint.length;i++){
sout(split[i]);
}
//最终输出
/*
E:
aaa
bbb
*/
toCharArray解释:
s = "happy";
char[] chars = s.toCharArray();
for(i<0;i<chars.length;i++){
sout(chars [i]);
}
/*
最终输出:
h
a
p
p
y
*/
format解释:
//占位符有 :%s %c %d %.2f
String name = "jack";
int age = 10;
double score = 98.3/3;
char gender = '男';
// 该写法特别麻烦
String info = "名字:"+name+"年龄"+age+"成绩是:"+score+"性别是:"+gender;
sout(info);
//格式字符串
String info2 = String.format("名字是%s,年龄是%d,成绩是%.2f,性别是%c",name,age,score,gender);
sout(info2);
//除了这样,还有另外写法
String formatStr = "名字是%s,年龄是%d,成绩是%.2f,性别是%c";
String info2 = String.format(formartStr,name,age,score,gender);
sout(info2);
StringBuffer类
java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
很多方法与String相同,但是StringBuffer是可变长度的
StringBuffer是一个容器
特点:
StringBuffer是final类,不能被继承
实现了Serializable接口,可以保存到文件或者网络传输(串行化)
继承了抽象类AbstractStringBuffer
AbstractStringBuffer中的属性char[] value,存放字符序列,他不是final,因此存储数据存放在堆中
String VS StringBuffer
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址效率较低
private final char value[] - StringBuffer保存的是字符串变量,里面的值可以更改,每次的StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高
char[] value 存放在堆
简单理解:StringBuffer的字符内容是存放在 char[] value (堆)中,每次变化(增加/删除)都不用更新地址(创建新的对象),效率高于String
StringBuffer的构造器
StringBuffer() :构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符
StringBuffer(CharSequence seq) :public java.lang.StringBuffer(CharSequence seq)构造一个字符串缓冲区,它包含与指定的CharSequence相同的字符(了解)
StringBuffer(int capacity) // capacity[容量] :构造一个不带字符但是具有指定初始容量的字符串缓冲区,即堆char[]大小进行指定
StringBuffer(String str) :构造一个字符串缓冲区,并将其内容指定为字符串内容
package javaSEStudy.wrapper.StringBuffer_;
// StringBuffer 构造器的使用
public class StringBuffer_Constructor {
public static void main(String[] args) {
// 1.创建一个大小为16的char[] 数组,用于存放字符内容
StringBuffer stringBuffer1 = new StringBuffer();
// 2.通过构造器指定char[] 大小
StringBuffer stringBuffer2 = new StringBuffer(100);
// 3.通过给一个String 创建StringBuffer ,char[] 大小就是str.length+16
StringBuffer stringBuffer3 = new StringBuffer("java");
}
}
String和StringBuffer转换
package javaSEStudy.wrapper.StringBuffer_;
// String 和 StringBuffer 转换
public class StringAndStringBuffer {
public static void main(String[] args) {
String str = "hello world";
//Sting ---> StringBuffer
// 1.使用构造器,ps:这里只有接收返回的数据buffer才是StringBuffer,str本身依旧是String类型
StringBuffer buffer1 = new StringBuffer(str);
// 2.使用append方法
StringBuffer buffer2 = new StringBuffer();
buffer2 = buffer2.append(str);
//StringBuffer ---> String
StringBuffer buffer3 = new StringBuffer("基尼太美");
// 1.使用StringBuffer提供的toString方法
String str1 = buffer3.toString();
// 2.使用构造器
String str2 = new String(buffer3);
}
}
StringBuffer常见方法
- append :增
- delete(start,end) :删
- replace(start,end,string) :改,将Start -- end 间的内容替换掉,不含end,左闭右开区间
- indexOf :查,查找子串在字符串第一次出现的索引,如果找不到就返回-1
- insert :插
- length :获取长度
- reverse() :字符序列反转
package javaSEStudy.wrapper.StringBuffer_;
// StringBuffer 方法
public class StringBuffer_Method {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer("java");
// 增 append:追加
stringBuffer.append(',');
stringBuffer.append("张三丰");
stringBuffer.append("赵敏").append(100).append(true).append(10.5);
System.out.println(stringBuffer);
// 输出 java,张三丰赵敏100true10.5
// 删
/**
* 删除索引为 >=start && < end 这区间内的字符
*/
stringBuffer.delete(10,13);
System.out.println(stringBuffer);
// 输出 java,张三丰赵敏true10.5
// 改 也就是替换 在这里是替换8 - 10区间内的内容
stringBuffer.replace(8,10,"周芷若");
System.out.println(stringBuffer);
// 输出 java,张三丰周芷若true10.5
// 查
int indexOf = stringBuffer.indexOf("张三丰");
System.out.println(indexOf);
// 输出 5
// 插 在索引下标为8的位置插入内容,原来该位置的内容后移
stringBuffer.insert(8,"赵敏");
System.out.println(stringBuffer);
// 输出 java,张三丰赵敏周芷若true10.5
// 长度 将字符串的总长度输出
System.out.println(stringBuffer.length());
}
}
小题
第一问

第一个sout会输出四,因为子啊append方法中会将null当做一个字符串数组输出,因此输出4
第二个sout输出null
第三个直接报空指针异常,因为创了一个新的对象,但是传入的str为null,它调用的是super.length() +16 ,这里为空,所以直接报空指针异常
第二问

package javaSEStudy.wrapper.StringBuffer_;
import java.util.Scanner;
public class Sell {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入商品名和价格");
String name = scanner.nextLine();
Double price = scanner.nextDouble();
StringBuffer priceBuffer = new StringBuffer(String.format("%.2f", price));
int index = priceBuffer.lastIndexOf(".");
for (int i = index-3; i >0 ; i -= 3) {
priceBuffer.insert(i,",");
}
System.out.println("商品名 商品价格");
System.out.print(name+"\t\t");
System.out.println(priceBuffer);
}
}
StringBuilder类
基本介绍:
- 一个可变的字符序列,此类提供一个与StringBuffer兼容的API接口但是不保证同步(StringBuilder线程不安全),此类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先使用该类,因为在大多数实现中,他比StringBuffer快
- 在StringBuilder上的主要操作是append方法和insert方法,可重载这些方法,以接受任意类型数据
StringBuilder继承了AbstractStringBuilder 类
它实现了 Serializable ,说明StringBuilder对象是可以串行化的
StringBuilder是final ,不可被继承
StringBuilder 对象字符序列仍然是存放在其父类AbstractStringBuilder的 char[] value 中,放在堆中
StringBuilder 的所有方法没有做互斥处理(Synchronized),多线程不安全,单线程使用
String StringBuffer StringBuilder 比较
- StringBuilder和StringBuffer非常类似,均代表可变字符序列,而且方法也一样
- String:不可变字符序列,效率低,但是复用率高
- StringBuffer:可变字符序列,效率较高(增删),线程安全
- StringBuilder:可变字符序列,效率最高,线程不安全
String使用说明:
String s = "a"; //创建了个字符串
s += "b"; //实际上原来的"a"已经被丢弃,现在又产生了一个字符串s+"b"(也就是"ab"),如果多次执行改变这些串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率,如果这些操作放到循环中会极大影响程序性能
//结论:如果我们需要对String做大量修改,不要使用String
String StringBuffer Stringbuilder的选择
1.如果字符串存在大量修改操作,一般使用StringBuffer或StringBuilder
2.如果字符串存在大量修改操作,并且存在单线程,使用StringBuilder
3.如果字符串存在大量修改操作,并且存在多线程,使用StringBuilder
4.如果字符串很少修改,被多个对象引用,使用String,比如配置信息等
Math类
Math类包含用于执行基本用于数学运算的方法,如:初等指数,对数,平方根和三角函数
常用方法:
- abs :绝对值
- pow :求幂
- ceil :向上取整
- floor :向下取整
- round :四舍五入
- sqrt :求开方
- random :求随机数
- max :求两个数最大值
- min :求两个数最小值
获取a~b之间的一个随机整数
公式: (int)(a + Math.random() * (b-a + 1))
package javaSEStudy.conection.math;
// Math类的方法
public class MathMethod {
public static void main(String[] args) {
// 基本都是静态方法,可以直接用
// 1.abs 绝对值
int abs = Math.abs(-10);
System.out.println(abs); // 返回10
// 2.pow 幂
double pow = Math.pow(2 ,4); // 2的四次方
System.out.println(pow); // 返回16.0
// 3.ceil 向上取整,返回 >=该参数的最小整数(转成double类型)
double ceil = Math.ceil(10.1); // ceil 上限,天花板
System.out.println(ceil); // 返回11.0,如果参数是10,返回10.0
// 4.floor 向下取整,返回 <=该参数的最大整数(转成double)
double floor = Math.floor(-4.00001); // floor 地板,地面
System.out.println(floor); // 返回-5.0
// 5.round 四舍五入
long round = Math.round(-5.0001);
System.out.println(round); // 返回-5
// 6.sqrt 开方,负数没有平方根
double sqrt = Math.sqrt(-9);
System.out.println(sqrt); // 输出 NaN not a number
// 7.random 随机数
/**
* random 返回的是0~1之间的一个随机小数 [0,1)
* 当我们想输出两个整数之间的数时
* 公式: (int)(a + Math.random() * (b-a + 1))
*/
System.out.println("标准随机数输出样式:");
for (int i = 0; i < 10; i++) {
System.out.print(Math.random()+" ");
}
// 那获取 a ~ b 之间的随机整数,a b 均为整数 [a ,b]
// 可理解为 (int)(a) <= x <= (int)(a + Math.random() * (b-a + 1))
// Math.random() * (b-a+1) ---> 返回的值为 0 <= x <= b-a+1
// 带入具体值为: a =2,b=7
// (int)(2 + Math.random() * (5 + 1))
// 所以上述表达式为 2 <= x < 8
// 前面可以取到2,但是后面因为小于8,可以取值7.16等,取整,最终输出 2 <= x <= 7
System.out.println("\n取2~7间的随机整数");
for (int i = 0; i < 10; i++) {
System.out.print((int)(2 + Math.random()*(7-2+1))+" ");
}
System.out.println("\n");
// 8.max min 返回两个数之间的最大值和最小值
System.out.println(Math.max(1,9));
System.out.println(Math.min(1,9));
}
}
Arrays类
Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)
常见方法一 :
- toString :返回数组的字符串形式
- sort :自然排序和定制排序
- binarySearch 通过二分搜索法进行查找,要求必须排好序,返回值为索引
package javaSEStudy.conection.arrays_;
import java.util.Arrays;
import java.util.Comparator;
// Arrays类方法
public class ArraysMethod1 {
public static void main(String[] args) {
Integer[] array1 = {1, 2, 3, 4, 5};
// 遍历数组
for (Integer i : array1) {
System.out.println(i);
}
// Arrays类提供toString方法直接显示数组信息
System.out.println(Arrays.toString(array1));
// sort 方法的使用
Integer[] array2 = {1, -2, 3, 8, 10, -4, 5};
// 1.可以使用冒泡排序 如下
// 2.Arrays类中有sort排序方法
// 注意: 因为数组是引用类型,所以通过sort排序后,会影响到实参arrays2
Arrays.sort(array2);
System.out.println(Arrays.toString(array2));
// 3.sort 是重载的,也可以通过传入接口 Comparator(比较器) 实现定制排序
// 调用定制排序时,传入两个参数,第一个参数为排序数组,第二个为实现了Comparator接口的匿名内部类
// 该匿名内部类要求实现 compare 方法
Arrays.sort(array2,new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
// 这里 o2 - o1 就可以实现从大到小排序
return o2 - o1;
}
});
System.out.println(Arrays.toString(array2));
// 这里体现了接口编程+动态绑定+匿名内部类综合使用的好处
// 4.binarySearch 通过二分搜索法(二叉查找)进行查找,要求必须排好序,查找数组中有没有某个数
// 注意,该方法返回值的值是索引,索引从0开始
// 如果数组中不存在该元素则返回 -(low + 1),意思是如果找不到该数,就返回这个数应该在的位置,但是为负,因为该数不存在
// +1是因为让索引从1开始,符合人们排序习惯
System.out.println(Arrays.binarySearch(array1,1));
}
// 冒泡排序
public static Integer[] sort1(Integer[] array2) {
int temp = 0;
boolean flag = false;
for (int i = 0; i < array2.length-1; i++) {
for (int j = 0; j < array2.length-1-i; j++) {
if (array2[j+1] < array2[j]) {
temp = array2[j];
array2[j] = array2[j+1];
array2[j+1] = temp;
flag = true;
}
}
if (!flag) {
break;
}
}
return array2;
}
}

这是定制排序的位置,调用了Comparator接口,然后重写compare方法,这里的return返回值为正还是为负,直接影响到binarySearch方法中的while,也就是下图中的方法

为正数走if方法,为负数走else方法,就实现将其倒置,while也是调用了compare方法,所以这里的compare的正负值直接影响到排序
如果理解不深刻,我们自己写一个排序方法
package javaSEStudy.conection.arrays_;
import java.util.Arrays;
import java.util.Comparator;
// 自己写一个定制的数组排序
public class ArraysSortCustom {
public static void main(String[] args) {
int[] array = {1, 21, 13, 45, 35, 0, 17};
// bubble_sort1(array);
bubble_sort2(array, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer)o1;
Integer i2 = (Integer)o2;
// 这里返回的值的正负决定下面是否大于0
return i2 - i1;
}
});
System.out.println(Arrays.toString(array));
}
// 使用冒泡进行排序
public static void bubble_sort1(int[] array) {
int temp = 0;
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
// 从小到大
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
// 结合冒泡排序 + 定制
public static void bubble_sort2(int[] array, Comparator c) {
int temp = 0;
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
// 数组排序由 c.compare(array[j], array[j + 1]) 返回的值决定
if (c.compare(array[j], array[j + 1]) > 0) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
}
常见方法二 :
- copyOf :数组元素复制
- fill :数组元素填充
- equals :比较两个数组元素内容是否完全一致
- asList :将一组值转换为list
package javaSEStudy.conection.arrays_;
import java.util.Arrays;
import java.util.List;
// Arrays类方法
public class ArraysMethod2 {
public static void main(String[] args) {
Integer[] array1 = {1, 21, 13, 45, 35, 0, 17};
/**
* copyOf 数组元素复制
* 表示从array数组中拷贝 array1.length 个数据到newArray中
* 如果拷贝长度大于原始数组,就在copy的数组后加null
* 如果拷贝0个,就是一个空数组
* 如果拷贝长度 < 0 ,就会抛出异常 NegativeArraySizeException 负数组大小异常
* 该方法的底层调用的是 System.arraycopy
*/
Integer[] newArray1 = Arrays.copyOf(array1, array1.length);
Integer[] newArray2 = Arrays.copyOf(array1, array1.length + 1);
Integer[] newArray3 = Arrays.copyOf(array1, array1.length - 1);
Integer[] newArray4 = Arrays.copyOf(array1, 3);
System.out.println("数组复制 "+Arrays.toString(newArray4));
/**
* fill 数组填充
* 使用99去将num中所有的元素替换为99
*/
Integer[] num = new Integer[]{1, 2, 5, 0, 8, 30, 23};
Arrays.fill(num, 99);
System.out.println("数组填充 "+Arrays.toString(num));
/**
* equ 比较两个数组中的元素是否相等
* 如果两个数组的元素是一样的,则返回true
* 注意:如果只是数组顺序不一样,但是内容一样,会返回false
*/
Integer[] array2 = {21, 1, 13, 45, 35, 0, 17};
boolean equ = Arrays.equals(array1, array2);
System.out.println("数组比较 "+ equ);
/**
* asList 将一组值转换为list
* 会将array1数据转成一个List集合
* 返回的 list1 编译类型 List(接口)
* list1 的运行类型 java.util.Arrays$ArrayList(在Arrays类里的 ArrayList静态内部类)
*/
List<Integer> list1 = Arrays.asList(array1);
System.out.println("数组转换为list "+list1);
System.out.println("运行类型 "+list1.getClass());
}
}
练习题

package javaSEStudy.conection.arrays_.test;
import java.util.Arrays;
import java.util.Comparator;
// 练习 自定义Book类
class Application {
public static void main(String[] args) {
Book[] books = new Book[4];
books[0] = new Book("红楼梦", 100);
books[1] = new Book("新金平梅", 90);
books[2] = new Book("新青年文选", 5);
books[3] = new Book("java从入门到放弃", 300);
/**
* 按照价格从小到大
*/
// Arrays.sort(books, new Comparator<Book>() {
//
// @Override
// public int compare(Book b1, Book b2) {
// return Double.compare(b1.getPrice(), b2.getPrice()); // 升序
// return Double.compare(b2.getPrice(), b1.getPrice()); // 降序
// }
// });
/**
* 按照价格从大到小
*/
// Arrays.sort(books,new Comparator<Book>() {
//
// @Override
// public int compare(Book b1, Book b2) {
// double d1 = b1.getPrice();
// double d2 = b2.getPrice();
// if (d1 > d2){
// return -1;
// }else if (d1 < d2){
// return 1;
// }else {
// return 0;
// }
// }
// });
/**
* 书名长度从大到小
*/
Arrays.sort(books, new Comparator<Book>() {
@Override
public int compare(Book b1, Book b2) {
// String s1 = b1.getName();
// String s2 = b2.getName();
// return s2.length() - s1.length();
return Integer.compare(b2.getName().length(), b1.getName().length());
}
});
for (Book book : books) {
System.out.println(book);
}
}
}
public class Book {
private String name;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "name='" + name + '\'' + ", price=" + price;
}
}
System类
常见方法:
- exit :退出当前程序
- arraycopy :复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组
System.arraycopy(src,0,dest,0,3); - currentTimeMillens :返回当前时间距离1970-1-1 的毫秒数
- gc :运行垃圾回收机制
System.gc();
package javaSEStudy.common.system_;
import java.util.Arrays;
// System类方法
public class SystemMethod {
public static void main(String[] args) {
// exit 退出当前程序
System.out.println("111111111");
// 0 代表正常退出
// System.exit(0);
System.out.println("222222222");
// arraycopy :复制数组元素,比较适合底层调用
int[] src = {1,2,3};
// dest 当前状态为{0,0,0}
int[] dest = new int[3];
/**
* src – 源数组
* srcPos – 从源数组的哪个索引开始拷贝
* dest – 目标数组
* destPos – 把源数组的数据拷贝到目标数组的哪个索引
* length – 从源数组拷贝多少个元素到目标数组
*/
// 倘若length长度大于源数组,则会抛出数组下标越界异常
System.arraycopy(src, 0, dest, 0, src.length-1);
System.out.println(Arrays.toString(dest));
// currentTimeMillens :返回当前时间距离1970-1-1 的毫秒数
System.out.println(System.currentTimeMillis());
// gc :运行垃圾回收机制
System.gc();
}
}
gc垃圾回收机制详解
垃圾回收(Garbage Collection, GC)是现代编程语言内存管理的核心机制,它自动回收不再使用的内存空间,减轻了开发者的负担。
基本定义:GC是自动内存管理系统,负责在程序运行时识别并回收不再被引用的对象所占用的内存空间。
核心功能:
- 分配内存给新对象
- 识别哪些内存块仍在被使用
- 回收不再被使用的内存块
关键优势:
- 防止内存泄漏
- 避免悬垂指针问题
- 减少手动内存管理错误
- 提高开发效率
复制算法(Copying)
原理:将内存分为两个半区,只使用其中一个,GC时将存活对象复制到另一半区
特点:
- 适合存活对象少的场景
- 无内存碎片
- 空间利用率仅50%
分代收集算法(Generational)
理论基础:弱分代假说(大多数对象很快死亡)和强分代假说(老对象很少引用新对象)
分代划分:
- 新生代(Young Generation):新创建对象
- 老年代(Old Generation):长期存活对象
- 永久代(Perm Generation,Java8前)/元空间(Metaspace,Java8+)
收集策略:
- 新生代:通常使用复制算法
- 老年代:通常使用标记-清除或标记-整理
复制算法与分代收集算法详解
一、复制算法(Copying Algorithm)
- 基本原理
- 内存划分:将可用内存分为大小相等的两个半区(From空间和To空间)
- 运行机制:
- 新对象始终分配在From空间
- 当From空间满时,暂停应用线程(STW)
- 将From空间的存活对象复制到To空间
- 清空整个From空间
- 交换From和To空间的角色
- 核心特点
- 空间效率:始终只有一半内存可用(空间利用率50%)
- 时间效率:仅需遍历存活对象,时间复杂度O(存活对象数)
- 无碎片化:对象在To空间连续排列
- 适用场景:对象存活率低的区域(如新生代)
- 优化变种
- Appel式回收:将新生代划分为Eden+2个Survivor区(8:1:1比例)
- 多空间复制:某些JVM实现采用3个空间轮转
二、分代收集算法(Generational Collection)
- 理论基础
- 弱分代假说:绝大多数对象"朝生夕死"(IBM研究显示Java应用中98%对象在Young GC时死亡)
- 强分代假说:老年代对象很少引用新生代对象(跨代引用<1%)
- 代际划分
| 代名称 | 占比 | 对象特征 | 回收频率 | 算法选择 |
|---|---|---|---|---|
| 新生代 | 1/3堆 | 新创建对象,存活时间短 | 高 | 复制算法 |
| 老年代 | 2/3堆 | 经历多次GC仍存活的对象 | 低 | 标记-清除/整理 |
| 永久代 | 固定 | 类元数据、常量等(JVM内部使用) | 极低 | 特定回收策略 |
- 跨代引用处理
- 记忆集(Remembered Set):记录老年代到新生代的引用
- 卡表(Card Table):将堆划分为512字节的卡,脏卡标记跨代引用
- 写屏障(Write Barrier):在对象引用修改时维护记忆集
-
典型工作流程
-
新生代回收(Minor GC):
- 触发条件:Eden区满
- 过程:存活对象从Eden+From Survivor复制到To Survivor
- 年龄计数:对象每存活一次GC年龄+1,达到阈值(默认15)晋升老年代
-
老年代回收(Full GC):
- 触发条件:老年代空间不足或元空间不足
- 过程:通常采用并发标记-清除算法
三、算法对比与选型建议
| 比较维度 | 复制算法 | 分代收集算法 |
|---|---|---|
| 内存开销 | 50%利用率 | 整体约85%利用率 |
| 执行效率 | O(存活对象) | 新生代O(存活),老代O(全部) |
| 停顿时间 | 较短 | 新生代短,老年代可能较长 |
| 适用场景 | 对象存活率<10% | 符合分代假设的所有场景 |
| 碎片问题 | 无 | 老年代可能有碎片 |
选型建议:
- 内存敏感场景慎用纯复制算法
- 面向响应式系统优先选择分代+G1/ZGC
- 物联网设备考虑分代+Serial收集器
总结
复制算法分了两个区,一个from区,一个to区,新的对象总是在from区,当from区满了,就暂停应用线程,将from区对象复制到to区,再清空from区,此时to区变成from区,from区变成to区
注意:实际应用中,约有98%对象在第一次GC死亡,可能只有2%对象需要复制到to区
// 典型内存布局
YoungGen = Eden + Survivor0 + Survivor1 // 比例通常8:1:1
这行代码展示了JVM虚拟空间比例,只有10%空间(Survivor)用于复制,90%(Eden)用于分配
空间溢出
空间溢出(Space Overflow)是指程序在运行时因内存分配不当或资源耗尽,导致无法继续执行正常逻辑的情况。常见于堆内存溢出(Heap Overflow)、栈溢出(Stack Overflow)、内存泄漏(Memory Leak)等场景。以下从定义、成因、影响、检测与修复四个维度详细解析。
- 空间溢出的类型与定义
(1) 堆内存溢出(Heap Overflow)
- 定义:程序在堆(Heap)上申请的内存超过JVM/系统限制(如
-Xmx设定的最大堆大小)。 - 典型错误:
- Java:
java.lang.OutOfMemoryError: Java heap space - C/C++: 程序崩溃或返回
NULL指针
- Java:
(2) 栈溢出(Stack Overflow)
- 定义:线程的调用栈深度超过限制(如递归无终止条件)。
- 典型错误:
- Java:
java.lang.StackOverflowError - C/C++: 段错误(Segmentation Fault)
- Java:
(3) 内存泄漏(Memory Leak)
- 定义:程序持续分配内存但未释放,最终耗尽可用内存。
- 典型表现:
- 长时间运行后内存占用持续上升
- 最终触发
OutOfMemoryError
- 空间溢出的主要成因
(1) 堆内存溢出的常见原因
- 大对象分配(如加载超大文件到内存)
- 缓存未清理(如无限制的
HashMap缓存) - 集合类无限增长(如未限制的
ArrayList或LinkedList)
(2) 栈溢出的常见原因
- 无限递归(如未设置终止条件的递归函数)
- 过深的函数调用链(如复杂嵌套调用)
- 线程栈空间过小(如JVM参数
-Xss设置不合理)
(3) 内存泄漏的常见原因
- 静态集合持有对象(如
static Map缓存未清理) - 未关闭资源(如数据库连接、文件流未
close()) - 监听器未注销(如事件监听器未移除)
- 空间溢出的影响
| 类型 | 影响范围 | 典型后果 |
|---|---|---|
| 堆溢出 | 整个JVM/进程崩溃 | 服务不可用,需重启 |
| 栈溢出 | 当前线程终止 | 部分功能失效,可能影响整体稳定性 |
| 内存泄漏 | 渐进式内存耗尽 | 长时间运行后系统变慢或崩溃 |
- 检测与修复方法
(1) 检测工具
- 堆Dump分析(MAT、VisualVM、JProfiler)
- 栈跟踪(
jstack、GDB调试) - 内存监控(Prometheus + Grafana监控堆/栈使用率)
(2) 修复策略
- 堆溢出:
- 优化大对象存储(如流式处理替代全量加载)
- 调整JVM参数(
-Xmx、-Xms)
- 栈溢出:
- 限制递归深度(改用迭代)
- 增加栈空间(
-Xss2m)
- 内存泄漏:
- 使用弱引用(
WeakHashMap) - 确保资源释放(
try-with-resources)
- 使用弱引用(
BigInteger类和BigDecimal类
应用场景:
- BigInteger :适合保存比较大的整型
- BigDecimal :适合保存精度更高的浮点型(小数)
BigIntegerl类方法
- add :加法
- subtract :减法
- multiply :乘法
- divide :除法
package javaSEStudy.common.bignum;
import java.math.BigInteger;
public class BigInteger_ {
public static void main(String[] args) {
// 当我们实际应用中数据过大long无法保存,如下
// long l = 1222222222222222244851;
/**
* BigInteger 可以保存超大数据,但是是以字符串形式保存,所以需要加上双引号
* 处理逻辑:以字符串形式保存,然后计算或输出在转换成数字
* 需要进行加减乘除,需要使用对应方法,不能直接 + - * /
*/
BigInteger bigInteger1 = new BigInteger("299999999999999999999999999");
BigInteger bigInteger2 = new BigInteger("299991554981654891515699999");
System.out.println(bigInteger1);
// 如下写法报错
// System.out.println(bigInteger+1);
// 加法
System.out.println("加法 "+bigInteger1.add(bigInteger2));
// 减法
System.out.println("减法 "+bigInteger1.subtract(bigInteger2));
// 乘法
System.out.println("乘法 "+bigInteger1.multiply(bigInteger2));
// 除法
System.out.println("除法 "+bigInteger1.divide(bigInteger2));
}
}
BigDecimal类方法
- add :加法
- subtract :减法
- multiply :乘法
- divide :除法
package javaSEStudy.common.bignum;
import java.math.BigDecimal;
import java.math.RoundingMode;
// BigDecimal 类用法
public class BigDecimal_ {
public static void main(String[] args) {
// 下面d输出的时候会输出1952.9521991111112
// 无法达到想要的精度
double d = 1952.952199111111111111111111111d;
System.out.println(d);
/**
* 使用BigDecimal 存储高精度小数
* 对BigDecimal进行加减乘除 需要使用特定的方法,也不能直接运算
* 创建一个需要操作的BigDecimal,在调用相应方法
*/
BigDecimal bigDecimal1 = new BigDecimal("1952.952199111111111111111111111");
BigDecimal bigDecimal2 = new BigDecimal("1.2");
System.out.println(bigDecimal1);
// 加法
System.out.println("加法 "+bigDecimal1.add(bigDecimal2));
// 减法
System.out.println("减法 "+bigDecimal1.subtract(bigDecimal2));
// 乘法
System.out.println("乘法 "+bigDecimal1.multiply(bigDecimal2));
// 除法
// 注意 如果该数除不尽,会造成死循环,抛出该异常:ArithmeticException: Non-terminating decimal expansion;
// 解决:指定精度 BigDecimal.ROUND_CEILING ---> 如果有无限小数,就保留和分子一样的精度
System.out.println("除法 "+bigDecimal1.divide(bigDecimal2,BigDecimal.ROUND_CEILING));
// 注意: 上述解决方法在jdk9+已被废弃,新版本写法:BigDecimal.RoundingMode.CEILING
System.out.println("除法 "+bigDecimal1.divide(bigDecimal2, RoundingMode.CEILING));
}
}
日期类 (知道怎么查,怎么用即可)
第一代日期类
- Date :精确到毫秒,代表特定瞬间
- SimpleDateFormat :格式和解析日期的类
它允许进行格式化(日期-->文本),解析(文本-->日期)和规范化
日期字母一览表

package javaSEStudy.common.date_;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
// 第一代日期类 详解
public class Date_ {
public static void main(String[] args) throws ParseException {
// 获取当前系统时间
// 这里的包是java.util.Date,不是java.sql.Date
// 默认输出格式为国外方式,通常需要进行格式转换
Date d1 = new Date();
// System.out.println("当前系统时间"+d1);
// 格式转换
// 创建 SimpleDateFormat 对象,可以指定相应格式,E是星期
// 这里使用的字母是规定好的,不可以乱写
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format1 = sdf.format(d1);
System.out.println("当前系统时间: "+format1);
// 通过指定毫秒数获取指定时间
// 这里显示1970年的时间
Date d2 = new Date(962512655);
String format2 = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E").format(d2);
System.out.println("指定获取时间: "+format2);
// 可以把一个格式化的String 转成对应的 Date
// 得到的Date仍然在输出时是国外的形式,如果希望得到指定格式仍然需要进行格式转换
// 得到的String--> Date,使用sdf格式需要和给的String格式一样,否则会抛出转换异常
String s = "2025年5月12日 17:57:58 周一";
Date parse = sdf.parse(s);
System.out.println("parse: "+sdf.format(parse));
}
}
第二代日期类
第二代日期类,主要就是Calendar类(日历)
Calendae类主要是一个抽象类,他为特定瞬间与一组诸如YEAR,MONTH,DAY_OF_MONTH,HOUR 等日历字段之间转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法
package javaSEStudy.common.date_;
import java.util.Calendar;
// 第二代日期类Calendar详解
// Calendar:日历类
public class Calendar_ {
public static void main(String[] args) {
/**
* Calendar是一个抽象类,并且构造器也是私有的
* 可以通过getInstance()获取实例
* 提供了大量的方法和字段供程序员使用
* Calendar没有提供格式化方法使用
*/
// 创建一个日历类对象
Calendar cal = Calendar.getInstance();
// System.out.println(cal);
// 年
System.out.println(cal.get(Calendar.YEAR));
// 月 这里威慑+1? 因为Calendar返回月份的时候从0开始编号
System.out.println(cal.get(Calendar.MONTH)+1);
// 日(一个月中的第几号)
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
// 小时
System.out.println(cal.get(Calendar.HOUR));
// 分钟
System.out.println(cal.get(Calendar.MINUTE));
// 秒
System.out.println(cal.get(Calendar.SECOND));
// Calendar没有专门的格式化方法,所以需要程序员自己组合
System.out.println(cal.get(Calendar.YEAR)+"年"
+(cal.get(Calendar.MONTH)+1)+"月"
+cal.get(Calendar.DAY_OF_MONTH)+"日");
}
}
第三代日期类
前面两代不足分析:
jdk1.0中包含了一个java.util.Date类,但是他的大多数方法已经在JDK1.1引入Calendar类后被弃用,而Calendar也存在问题
- 可变性 :像日期和时间这样的类应该是不可变的
- 偏移性 :Date中的年份是从1900开始的,而月份都是从0开始的
- 格式化 :格式化只对Date有用,Calendar 则不行
- 此外 :他们也不是线程安全的,不能处理闰秒等(每隔2天多1秒)
第三代日期常见方法(JDK8加入):
- LocalDate(日期) :只包含日期,可以获取日期字段
- LocalTime(时间) :只包含时间,可以获取时间字段
- LocalDateTime(日期时间) :包含日期+时间,可以获取日期和时间字段
- DateTimeFormatter :格式日期类
- Instant :时间戳,类似于Dae,提供一系列和Date转换的方式
Instant-->Date
Date date = Date.from(instant)
Date-->Instant
Instant instant = date .toInstant() - MonthDay类 :检查重复事件
- 增加日期的某个部分
- 使用plus方法测试增加时间的某个部分
- 使用minus方法测试查看一年前和一年后的日期
- 还有很多时间,使用的时候查看api
package javaSEStudy.common.date_;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
// 第三代日期类使用
public class LocalDate_ {
public static void main(String[] args) {
/**
* 第三代日期
* 使用now(),返回当前日期时间的 对象
*/
// LocalDate.now() LocalTime.now()
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// 获取年份
System.out.println(ldt.getYear());
// 获取月份 这里是英文月份
System.out.println(ldt.getMonth());
// 获取月份,数字表示
System.out.println(ldt.getMonthValue());
// 获取日
System.out.println(ldt.getDayOfMonth());
// 获取小时
System.out.println(ldt.getHour());
// 获取分钟
System.out.println(ldt.getMinute());
// 获取秒
System.out.println(ldt.getSecond());
/**
* 使用DateTimeFormatter对象来进行格式化
* 首先创建一个DateTimeFormatter对象
*/
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(ldt.format(formatter));
/**
* Instant 时间戳
* 通过静态方法now(),获取表示当前时间戳的对象
* 通过from方法可以把Instance转成Date
*/
Instant now = Instant.now();
System.out.println(now);
// from方法: Instant 转 Date
Date date = Date.from(now);
//toInstance方法: Date 转 Instant
Instant instant = date.toInstant();
/**
* 提供plus 和 minus 方法可以对当前时间加减
* 还有很多方法,使用的使用查看api
*/
// 查看增加了980天后的时间
LocalDateTime localDateTime1 = ldt.plusDays(980);
System.out.println(formatter.format(localDateTime1));
// 查看2456分钟前的时间
LocalDateTime localDateTime2 = ldt.minusMinutes(2456);
System.out.println(formatter.format(localDateTime2));
}
}
常用类测试
第一问

package javaSEStudy.common.test;
// 将字符串指定部分翻转
public class Test01 {
public static void main(String[] args) {
String s = "abcdef";
try {
s = reverse(s, 1, 4);
} catch (Exception e) {
System.out.println(e.getMessage());
return;
}
System.out.println(s);
}
/**
* 思路:
* 使用StringBuffer的reverse()翻转数组
* 在将其拼接输出即可
*/
// public static String reverse(String str, int start, int end) {
// String str1 = str.substring(start, end+1);
// StringBuffer reverse1 = new StringBuffer(str1).reverse();
// return str.substring(0, start) + reverse1 + str.substring(end+1);
// }
/**
* 老韩做法思路:
* String 是final类,需要转换为char[]才可以交换元素位置
* 两边各有数组元素开始交换,当左边交换的位置开始与右边重合,或者左边交换元素的位置大于右边
* 就代表此时元素已经完全交换完毕,再交换位置会导致将交换好的元素再换回去
*/
public static String reverse(String str, int start, int end) {
// 对输入参数做验证
// 重要编程思想 :
// 写出正确情况 然后取反
if(!(str != null && end > start && end < str.length())) {
throw new RuntimeException("参数错误");
}
char temp = ' ';
char[] str1 = str.toCharArray();
for (int i = start, j = end; i < j; i++, j--) {
temp = str1[i];
str1[i] = str1[j];
str1[j] = temp;
}
// 使用str1 重新构建一个String返回
return new String(str1);
}
}
第二问

自己写的版本
package javaSEStudy.common.test;
import java.util.Scanner;
import java.util.regex.Pattern;
// 注册用户 自己写的版本
public class Test02_PLUS {
public static void main(String[] args) {
/**
* 思路:
* 先创建data存储传入的数据
* 整行传入想输入的数据,正则表达式空格分割
* 输入数据分割为三个数组,分别取出保存然后传入data
* 最后增加异常抛出和对输入内容进行验证
*/
System.out.println("------用户注册------");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名(2-4位) 密码(仅支持数字) 邮箱,空格隔开");
String in = scanner.nextLine();
String[] input = in.split("\\s");
try {
if (input.length != 3) {
throw new RuntimeException("输入错误");
}
String name = input[0];
String passWord = input[1];
String email = input[2];
nameMethod(name);
passWordMethod(passWord);
emailMethod(email);
Data user = new Data(name, passWord, email);
System.out.println("注册成功!");
System.out.println(user);
} catch (NumberFormatException e) {
System.out.println("注册错误" + e.getMessage());
} finally {
scanner.close();
}
}
// 验证方法
// 用户名验证
public static void nameMethod(String name) {
// 用户名2-4位不为空取反则为错误条件
if (!(name != null && name.length() > 1 && name.length() < 5)) {
throw new RuntimeException("输入用户名错误");
}
}
// 密码验证
public static void passWordMethod(String passWord) {
// 正则表达式匹配只能为6位数字
if (!passWord.matches("\\d{6}")) {
throw new RuntimeException("密码必须为6位数字");
}
}
// 邮箱验证
public static void emailMethod(String email) {
// 偷个懒,直接找的网络上邮箱匹配的正则表达式
if (!Pattern.matches("^\\w+@[a-z0-9]+\\.[a-z]{2,3}$", email)) {
throw new IllegalArgumentException("邮箱输入错误");
}
}
}
class Data {
private String name;
private String passWord;
private String email;
public Data(String name, String passWord, String email) {
this.name = name;
this.passWord = passWord;
this.email = email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@Override
public String toString() {
return "用户名: '" + name + '\'' +
", 密码: " + passWord +
", 邮箱: '" + email + '\'';
}
}
老韩版本
package javaSEStudy.common.test;
// 老韩版本
public class Test02 {
public static void main(String[] args) {
String name = "jack";
String pwd = "123456";
String email = "jack@gmail.com";
try {
userRegister(name, pwd, email);
System.out.println("恭喜你!注册成功~");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
/**
* 编写方法userRegister
* 针对输入的内容进行校验,如果有问题就抛出异常
* 单独写个方法判断密码是否全是数字
*/
public static void userRegister(String name, String pwd, String email) {
if(name == null && pwd == null && email == null) {
throw new RuntimeException("输入错误");
}
// 首先验证用户名
int userLength = name.length();
if (!(userLength >= 2 && userLength <= 4)) {
throw new RuntimeException("用户名长度2~4位");
}
// 然后验证密码
if (!(pwd.length() == 6 && isDigital(pwd))) {
throw new RuntimeException("密码为6位纯数字");
}
// 最后验证邮箱
int i = email.indexOf('@');
int j = email.indexOf('.');
if (!(i > 0 && j > i)) {
throw new RuntimeException("邮箱格式错误");
}
}
// 验证全是数字方法
public static Boolean isDigital(String str) {
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++) {
// 在ASCII码中 只有0~9才是数字,超过这个范围则不为数字
if (chars[i] < '0' && chars[i] > '9') {
return false;
}
}
return true;
}
}
第三问

package javaSEStudy.common.test;
// 以英文形式输出人名
public class Test03 {
public static void main(String[] args) {
String name = "William Jefferson Clinton";
System.out.println(conversion(name));
// printName(name);
}
// 写一个将英文名转换的方法
public static String conversion(String str){
if(str == null){
System.out.println("数据不能为空");
return str;
}
String[] strs = str.split(" ");
if(strs.length != 3){
System.out.println("输入格式错误");
return str;
}
String surname = strs[0];
String name1 = strs[1];
String name2 = strs[2];
name1 = name1.substring(0, 1).toUpperCase() ;
// 使用StringBuffer对字符串进行拼接
StringBuffer result = new StringBuffer();
result.append(name2).append(",").append(surname).append(".").append(name1);
return result.toString();
}
/**
* 老韩方法
* 对字符串进行分割 split(" ")
* 对分割出来的字符串进行格式化 foemat
*/
public static void printName(String str){
if(str == null){
System.out.println("输入数据不能为空");
return;
}
String[] names = str.split(" ");
if(names.length != 3){
System.out.println("输入名字格式不正确");
return;
}
String format = String.format("%s,%s.%c", names[2], names[0], names[1].toUpperCase().charAt(0));
System.out.println(format);
}
}
第四问

package javaSEStudy.common.test;
// 判断字符串里有多少数字,大写字母,小写字母
public class Test04 {
public static void main(String[] args) {
String str1 = "T5hV9nP2qR7yK4wX8bS1";
judgmentString(str1);
}
/**
* @param str;
* 思路:
* 先创建三种字符计数的对象
* 然后使用for循环对其遍历获取所有值
* 使用charAt获取该位置字符
* 分别进行匹配是哪种并输出
*/
public static void judgmentString(String str) {
int digital = 0;
int uppercase = 0;
int lowercase = 0;
if(str == null) {
System.out.println("字符串不能为空");
return;
}
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c >= 'A' && c <= 'Z') {
uppercase++;
} else if (c >= 'a' && c <= 'z') {
lowercase++;
}else if (c >= '0' && c <= '9') {
digital++;
}else {
break;
}
}
System.out.println("字符串长度:"+str.length());
System.out.println("大写字母:" + uppercase);
System.out.println("小写字母:" + lowercase);
System.out.println("数字:" + digital);
}
}
第五问

答案 : f f t f f t
解析 :
有个动物类,里面有个构造器包含那么属性,s1在栈中,直接在常量池创建"hspedu",然后new了两个a和b对象 a和b 是new的两个对象,在堆中分别是两个空间,在指向常量池中的相同常量,1为f,第二个由于方法没有重写,本质也是计算a==b,所以f,第三个通过.name,指向的是常量池中同一个地址,f.后面简单不多解释
浙公网安备 33010602011771号