其它
API(Application Programing Interface)
应用程序接口(功能),我们java讲解最常用的一些功能。
API作用:API表示的是功能,学习API可以快速进行编程开发。
API设计初衷,设计者将复杂的业务逻辑,封装成方法,供调用者更好的使用。对于开发者而言,不需要关注功能的具体逻辑实现,只需要知道如何使用即可。
Java提供了很多的包,有一些包需要导入,有一些不需要导入:
1.java.lang包:Java的核心语言包,封装了Java最核心的API,使用时一般不需要导包 例如:String ,Object,System,Integer …
2.java.io 包:可以实现文件的输入和输出的功能.
3.java.net 包:网络相关的功能
4.java.util : 工具 例如Scanner就是在这个包中
String(字符串)
-
java.lang.String全包名,String这个类被final修饰的.
-
String内部封装了一个用final修饰的char数组,也就意味着我们使用的字符串,本质上是有字符数组组成,且不可被修改.
-
字符串一旦被创建,对象内容是不可变的,若重新为String类型变量赋值或拼接,都不会在原有基础上改变
-
String是引用类型.
//“123” 是字符串字面量,本质上就发生了创建String对象的操作
String a = "123";// 写法1
String c = new String("123"); //写法2
String b = a;
a = "456"; //每次修改都是在重新创建
本质上存储:
char [] a1 = {'1','2','3'}; ----
/**
* 字符串使用演示类
* */
public class StringDemo {
public static void main(String[] args) {
String a = "123";
String b = a;
a = "456";
System.out.println(a); // 456
System.out.println(b); // 123
System.out.println(a == b); // 这里判断的是内存地址
int a1 = 100;
int a2 = 100;
System.out.println(a1 == a2); //这里判断的是值
/**
* 引用类型的变量使用双等号进行判断,判断的是他的内存地址是否相同
* 基本数据类型的变量使用双等号判断,判断的是他的值是否相等
* 引用类型存地址 基本数据类型存数据
* */
}
}
内存图:
理解:
123会在堆中开辟一块String空间地址0x1,对象中有什么取决于类型中有什么(String类型中有一个char数组)内容是123,那么就会将其拆分存为{‘1’,‘2’,‘3’}存储,但是不是直接存储在堆中values里面,而是在堆中还有一块地地址9x1(但是里面并不是直接装数字,而是装对应的码),之后Sting 对象中的values对象引用9x1这个char数组对象, 之后再将String对象的地址0x1赋值给a
把a 赋值给b,那么也就是将堆中的0x1地址指向b (这里如果不做修改,那么a 和 b使用的都是同一地址0x1, 值也相同)
a = “456”; 这里修改,一定不会在原有基础上进行修改 其一就是使用的是char数组存储,但是数组是不可变的, 这里会在堆中重新开辟一块空间地址是0x2,也会再开辟一块中间存储内容地址9x2,然后指向 values,之后String对象地址0x2指向a, a就不再是0x1了
常量池
String对象一旦被创建,不可变的原因在于String的内容比较常用,设计者这么做就是为了优化内存,因为堆中有一个常量池的存在,常量池存放已经创建过的字符串内容的地址.
现象:当创建字符串对象时,系统会先去常量池中看,要创建的字符串对象内容存不存在,如果存在直接复用.
String c = "789";
String d = "789";
System.out.println(c == d); //true
内存图:
理解:
789会在堆中开辟一块String空间地址0x1 那么就会将其拆分存为{‘7’,‘8’,‘9’}存储,但是不是直接存储在堆中values里面,而是在堆中还有一块地地址9x1 之后Sting 对象中的values对象引用9x1这个char数组对象, 之后再将String对象的地址0x1赋值给c
之后还会将这个String对象地址0x1存储在常量池中
String d = “789”; 之后再去创建这个时,会先检测常量池中是否有一个“789”这样的常量地址,有就不会再去创建,而是直接将常量池中存储的0x1这个地址直接指向d
String对象创建的方式有两种形式:
- 字符串创建的静态方式(都可复用)
以字符串字面量的形式来创建的字符串对象. 例如: String a = “123”;
字符串常量
以字符串字面的方式 进行拼接而成的字符串.
static final String C = "123";
String a = "123";
String b = "123";
String d = "1" + "23";
System.out.println(a);
System.out.println(b);
System.out.println(a == b);//true
System.out.println(a == C);//true
System.out.println(a == d);//true
- 字符串创建的动态方式(不可复用)
使用new关键字来创建的String对象. 例如: new String(“123”);
String类型的变量 拼接的字符串字面量.
//不可复用的动态创建方式:
String a = "123";
String e = new String("123");
System.out.println(a == e);//false
String f = "1";
String g = f + "23";
System.out.println(a == g);//false
equals
字符串内容判断是否相等,使用equals方法来完成
//在String 的判等操作中,我们更关心判断的内容是否相等,而并非内存地址是否相等
//我们使用equals 方法 即可实现.
System.out.println(a.equals(g));//true
System.out.println(“789”.equals(“789”));//true
面试题:
String str = new String(“123”);
问:上述代码创建了几个String对象?? 若问创建了几个对象 .
答:创建了2个String对象. 则是3个对象,2个String对象1个char数组对象
第一个对象是字符串字面量 “123”。字符串字面量在Java中是不可变的,它们通常会被缓存在字符串常量池(String Pool)中,以便复用。在这里,字符串池会检查是否已经有一个值为 “123” 的字符串对象存在,如果不存在,它将创建一个,并将其存储在池中。
第二个对象是通过 new String(“123”) 显式创建的。这将创建一个新的 String 对象,它的内容与第一个字符串字面量相同(都是 “123”),但它是一个新的对象,不与字符串池中的对象关联。这里的关键点是使用 new 关键字创建的字符串对象会在堆内存中分配,而不是在字符串常量池中。
StringAPI
String类提供了一些方法,例如:可以获取字符串的长度,可以通过索引查找对应的字符串中的字符内容,将字符串内容转换 大写,小写…
1.获取字符串中内容的长度,通过下标获取对应的字符内容
public static void main(String[] args) {
String str = "Thinking in java是一本好书";
//注意:这里的length是一个方法,而数组中的length是一个属性
System.out.println(str.length()); //获取 字符串内容长度的方法
System.out.println(str.charAt(20)); //通过传入的索引,返回对应字符串中的字符
// 练习: 编写一段程序 将Thinking in java是一本好书 内容中字符i 存在次数统计出来
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == 'i') {
count++;
}
}
System.out.println(count);
}
2.indexOf() 从首到尾 检索传入的字符/字符串,检索到后立刻返回对应的下标
lastIndexOf() 从尾到首 检索传入的字符/字符串,检索到后立刻返回对应的下标使用方式与indexOf一样
public static void main(String[] args) { String str = "Thinking in java是一本好书"; //检索字符串 // indexOf 从头到尾 int index = str.indexOf('i'); //该方法从首到尾检测每个字符是否等于‘i', 检测到立刻返回下标(返回第一个检测到的位置) System.out.println(index); //2 index = str.indexOf("java"); //该方法从首到尾检测传入的字符串是否等于java,检测到立刻返回对应字符串首字母下标,(返回第一个检测到的位置,如果没有返回-1) System.out.println(index); //12 index = str.indexOf('i',3); //这里的3指的是从索引为3的位置开始执行 System.out.println(index); //5 index = str.indexOf('x'); System.out.println(index); // -1 表示内容不存在 //lastIndexOf 从尾到首检索字符串 index = str.lastIndexOf('i'); //该方法从尾到首检测每个字符是否等于‘i', 检测到立刻返回下标(返回第一个检测到的位置) System.out.println(index); // 9 }
3.字符串转大小写, 去除两端空白, 判断以…开始 或以…结尾的方法。
public static void main(String[] args) {
String str = "Thinking in java是一本好书";
// 字符串换换位大小写方法
str = str.toUpperCase(); // 将字符串中的字母转换为大写
System.out.println(str); //THINKING IN JAVA是一本好书
str = str.toLowerCase(); // 将字符串中的字母转换为小写
System.out.println(str); //thinking in java是一本好书
String name = " Tom ";
System.out.println(name);
name = name.trim(); //去除两端空格的方法
System.out.println(name);
System.out.println(name.startsWith("T")); //是不是以T开头 ---返回值boolean
System.out.println(name.endsWith("m")); //是不是以m结尾 ---返回值boolean
}
4.截取字符串 subString , 将数值类型转换为String 的内容 valueOf();
public static void main(String[] args) {
// 截取字符串
String mail = "adjsabndasbfnhsdf@qq.com";
//substring() 第一个参数截取下标 第二个参数:结束截取的下标(包头不包尾)
// mail = mail.substring(0, 17);
// System.out.println(mail);
// 练习:如果想要截取一个通用用户名的程序,那么代码应该怎么写??
// 例如:传递liucs@qq.com ---截取liucs
// 例如:传递bjzhangpeng@qq.com ---截取bjzhangpeng
int endIndex = mail.indexOf('@'); //这里是根据@来截取,那么就先获取到@的索引
System.out.println(endIndex);
mail = mail.substring(0, endIndex);
System.out.println(mail);
// 截取字符串的用法两种:
// 1. subString(开始下标,结束下标) 截取中间的内容
// 2. subString(开始下标,开始下标+要截取的长度)
mail = mail.substring(0, 0+5); //代表从0开始截取5个长度的内容
System.out.println(mail); //adjsa
//String 提供了一个静态方法valueOf方法 可以将数值类型转换为String类型
int a = 123;
String b = String.valueOf(a);
System.out.println(b);
System.out.println(456); //本质上打印出来的数据都是String类型的(都被Sting.valueOf进行转换了)
}
5.判断字符串中是否包含某个字符串的内容, 判断字符串内容长度是否为0 ,将字符串对象转换为char数组
public static void main(String[] args) {
//判断字符串中是否包含 某个字符串的内容
String a = "AOP";
boolean r = a.contains("O"); //true
System.out.println(r);
String b = "";
// isEmpty 判断字符串长度是否为0
System.out.println(b.isEmpty()); //true
String c = "abcdefg";
char[] r1 = c.toCharArray(); //将字符串内容转换为char数组来进行存储
for (int i = 0; i < r1.length; i++) {
System.out.println(r1[i]);
}
}
StringBuilder
因为String本身不可变,但凡需要修改字符串内容,我们推荐使用StringBuilder来进行修改。
追加功能 插入功能 删除功能 修改功能
package stringdemo;
/**
* StringBuilder的使用演示
* */
public class StringBuilderDemo {
public static void main(String[] args) {
String a = "a";
long start = System.currentTimeMil lis(); //获取当前系统时间 单位毫秒
for (int i = 0; i < 100000; i++) {
a += "a";
// a += a; //直接报错OutOfMemoryError内存溢出
}
long end = System.currentTimeMillis();
System.out.println("运行时间是:" + (end - start) + "毫秒");
}
}
OutOfMemoryError内存溢出异常 -->内存中没有可用的空间了
上面使用a +=a 试图在一个循环中不断将字符串 a
的内容追加到自身,导致内存不断增加,最终耗尽了可用内存。
在这个循环中,每次执行 a += a
都会创建一个新的字符串,并将两个字符串连接起来,然后将结果分配给 a
。由于这个过程不断重复,新的字符串会越来越长,占用的内存也会不断增加,直到达到 JVM 的内存限制,导致内存溢出。
String不适合频繁修改的,一旦发生修改就是创建新的对象,频繁创建对象对内存消耗也是非常大的.
StringBuilder:
解决当如果对String进行频繁修改时,可以使用java提供的StringBuilder来代替完成.
因为StringBuilder内部维护了一个可变的char数组,修改性能小,而且对于字符串的一系列的增删改插都有提供对应的功能.
增删改插
public static void main(String[] args) {
String str = "好好学习java";
StringBuilder builder = new StringBuilder(str);
//append() 追加内容方法,将需要追加的内容写到小括号中
builder.append(",66666");
System.out.println(builder); // 好好学习java,66666 这里本质上是在原有基础上追加
// 字符串修改 替换的方法 1. 开始替换的下标 2.结束替换的下标 3. 替换的内容
builder.replace(9, 14 ,"人才");
System.out.println(builder); // 好好学习java,人才
// 删除的使用 delete() 1.开始删除的索引 2.结束删除的索引
builder.delete(0, 9);
System.out.println(builder); //人才
//插入的方法 insert 插入的使用: 1. 插入的索引位置 2. 插入的内容
builder.insert(0, "活着,");
System.out.println(builder); //活着,人才
//反转的方法
builder.reverse();
System.out.println(builder); //才人,着活
str = builder.toString(); //操作完成之后,可以将builder的内容转换为 String 对象
System.out.println(str); //才人,着活
}
.toString() 可以将StringBuilder转换为String
StringBuilder效率提升
public static void main(String[] args) {
String a = "a";
StringBuilder builder = new StringBuilder(a);
long start = System.currentTimeMillis(); //获取当前系统时间 单位毫秒
for (int i = 0; i < 100000; i++) {
// a += "a";
// a += a; //直接报错OutOfMemoryError内存溢出
builder.append(a);
}
long end = System.currentTimeMillis();
System.out.println("运行时间是:" + (end - start) + "毫秒"); //3毫秒
这里使用StringBuilder 效率会更高,也不会出现内存溢出,但是数据过大还是会出现的。
正则表达式
正则表达式作用就是用来检测一些格式是否按照规定的格式进行输入.
例如:邮箱的格式,座机号格式,身份证号格式…我们可以用正则来约束.
基本正则表达式
[] 表示的是一个字符的意思
[a,b,c] 表示的是可以写a或b或c的任意一个字符
[^abc] 表示的是可以写除了a或b或c的任意一个字符
[a-z] 表示的可以写一个26字母中任意一个字符
[a-zA-Z0-9] 表示的是可以写一个26小写字母/大写字母/0-9数字任意一个
[a-z&&[bc]] 表示的是26个小写字母,除了bc不能出现,其它都可以
预定字符集
. 表示是任意一个字符
\d 表示可以写一个任意的数字字符 0-9
\D 表示可以写一个非数子字符
\w 表示可以写a-zA-Z0-9的任意一个字符
\W 表示可以写非字母数字字符
\s 表示是一个空白字符
\S 表示的是不能写非空白字符
数量词
? 表示可以写0-1个
例如:[abc]? 只能写一个a b c中的内容 不能写其它内容
+ 代表至少出现一次 ,没有上限
例如:[abc]+ 表示可以写至少1个a b c, 可以出现多次, 不能出现其它内容
* 表示可以写任意次, 一次不写都可以
------------------------------------------------------------------------- 限制次数:
[abc]{3} 表示只能写3个字符
[abc]{3,5} 表示至少写3个,最多写5个
[abc]{3,} 至少写3个,没有上限
分组
(abc){3} 表示abc可以视为一组,出现3次
(abc|def){3} 表示abc 或者def 为一组, 出现3次
邮箱的正则表达式使用
package regexdemo;
/**
* 正则表达式的使用演示
* */
public class RegexDemo {
public static void main(String[] args) {
String mail = "asdafasfasfas@test.cn";
String regex = "[a-zA-Z0-9_]+@[a-z-A-Z0-9]+(\\.[a-zA-Z]+)+";
//matches 方法是通过传入正则表达式的格式,来匹配判断当前字符串对象中格式是否匹配上!
if (mail.matches(regex)){
System.out.println("是邮箱");
}else {
System.out.println("不是");
}
}
}
身份证号正则表达式演示
public static void main(String[] args) {
// 身份证号正则表达式演示
/**
* 身份证号---15位 或者18位
* 123456789123456
* 123456789123456789
* 12345678912345678X
* 12345678912345678x
* \d{15} -------------表示写15个 0-9的数字
* \d{18} -------------表示写18个0-9的数字
* \d{17}[\dXx] -------表示前面是17个数字,后面的第18位可以是数组,也可以是X或x字母
*
* \d{15}|\d{17}[\dXx]
* */
Scanner scanner = new Scanner(System.in);
System.out.println("请输入身份证号:");
String line = scanner.next();
String regex = "\\d{15}|\\d{17}[\\dXx]";
if (line.matches(regex)) {
System.out.println("身份证格式正确");
} else {
System.out.println("错误");
}
座机号码的正则格式使用
public static void main(String[] args) {
// 座机号码的正则练习 :
/**
* 1234567 --七位
* 12345678 --八位
* 010-1234567 ----区号+七位电话
* 010-12345678 ---三位区号+八位号码
* 0101-1234567 ----四位区号+七位号码
* 0101-12345678 ----四位区号+八位号码
* (010)1234567
* (010)12345678
* (0101)1234567
* (0101)12345678
* 正则格式:
* \d 表示 数字
* ? 表示 0 或 1 次
* --------------------------------
* \d{7,8} ---7位或八位的电话
* 区号两种情况: \d{3,4}-?
* 整体格式 \d{3,4}-?\d{7,8}
* () 在正则中表示 打组的意思 若需要表示是一个普通的,需要转义\\( \\)
* */
Scanner scanner = new Scanner(System.in);
System.out.println("请输入号码:");
String line = scanner.next();
String regex = "(\\d{3,4})-|\\(\\d{3,4}\\)?\\d{7,8}";
if (line.matches(regex)) {
System.out.println("正确");
} else {
System.out.println("错误");
}
}
StringAPI切割方法的使用:
package regexdemo;
/**
* StringAPI中支持正则的使用演示类
* */
public class RegexDemo02 {
public static void main(String[] args) {
String str = "abc123def456ghi789";
//split() 传入正则表达式,来分割
String[] data = str.split("[0-9]+"); //遇到 当前字符串 中存在连续1次以上的数字,来做分割点
System.out.println(data.length);
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
String str1 = "1-2-3-4-5-6-7-8"; //分割出1 2 3 4 5 6 7 8的数据装入数组中
String[] data1 = str1.split("-");
for (int i = 0; i < data1.length; i++) {
System.out.println(data1[i]);
}
String str2 = "123abc456def789ghi"; //分割出123 456 789的数据装入数组中
String[] data2 = str2.split("[a-z]+");
for (int i = 0; i < data2.length; i++) {
System.out.println(data2[i]);
}
}
}
StringAPI替换的功能使用:
替换方式两种:replaceAll replace
public static void main(String[] args) {
String regex = "(sb|tmd|dsb|qnmd|nc|wr|fuck|nmp)"; //敏感词字符
String message = "qnmd!你这个dsb,你怎么这么nc,fuck!"; //用户聊天内容
//replaceAll 替换全部内容: 1. 放正则表达式 2. 要替换的内容
message = message.replaceAll(regex,"小可爱"); //网络和谐用语
System.out.println(message); //小可爱!你这个小可爱,你怎么这么小可爱,小可爱!
String str = "abc123def456ghi789";
//需求:将str中内容 数值相关内容替换成NUMBER来表示
String regex1 = "[0-9]+";
str = str.replaceAll(regex1,"NUMBER");
System.out.println(str); //abcNUMBERdefNUMBERghiNUMBER
String str1 = "abc123edf456";
// replace 不支持正则: 1. 要匹配的内容 2. 要替换的内容(这里你写什么就给你替换什么)
str1 = str1.replace("123", "***"); //如果有多个123,那么就匹配多个,其它内容匹配不了
System.out.println(str1); //abc***edf456
}
待补充…
设计模式
常用的就23种
设计模式,遵循了开发时的原则,主要解决代码复用性,减低耦合度,增加可维护的目的。
设计模式 可以解决软件设计中很多问题,例如创建型问题…
注意:设计模式灵活度高,但消耗性能.
常用的设计模式:
创建型—> 单例模式,工厂模式,抽象工厂
单例设计模式
当在程序中编写一些工具类里面包含功能方法,工具类很常用.如果频繁创建对象,会造成内存的开销,如果认为工具类是独一无二的,那么就可以认为工具类只可以有一个实例对象。
单例模式可以实现一个类只有一个对象的存在。
单例模式模式有两种写法:
饿汉式:
不管外部有没有用到当前类的功能,当前类在运行初始时,就已经创建了对象供外部访问。
演示代码:
package singletondemo;
/**
* 饿汉式单例模式的使用演示类
*/
public class HungrySingletonDemo {
public static void main(String[] args) {
// 使用方式1
// HungrySingLeton h = HungrySingLeton.getInstance();
// System.out.println(h.getPI()); //3.14159265
// 使用方式2
System.out.println(HungrySingLeton.getInstance().getPI()); //3.14159265
}
}
/**
* 饿汉式单例模式 : 不管别人有没有调用获取对象的方法,我们自己已经创建好了
* 1. 构造方法私有化
* 2. 在类中自己创建对象
* 3. 对外提供访问对象的静态方法
* */
class HungrySingLeton { //单例-----> 饿汉式写法
//单例模式就是要防止外部创建对象,所以要在类中提供
//2. 本类创建唯一的对象,且共享这个对象
private static HungrySingLeton hungrySingLeton = new HungrySingLeton();
private HungrySingLeton() { //1. 私有化类的构造方法(不希望外部创建我这个对象)
}
public static HungrySingLeton getInstance() { // 3. 对外提供一个获取本类对象的方法
return hungrySingLeton;
}
public double getPI(){ //工具功能
return 3.14159265;
}
}
懒汉式:
当外部用到我这个类的功能时,我才创建对象供外部访问
优点:第一次使用时,才会创建对象,避免内存浪费.
缺点:存在线程安全(第二阶段会讲)
演示代码:
package singletondemo;
/**
* 懒汉式的使用演示类:
* */
public class LazySingletonDemo {
public static void main(String[] args) {
LazySingleton a = LazySingleton.getLazySingleton();
LazySingleton b = LazySingleton.getLazySingleton();
System.out.println(a == b);
}
}
/**
* 懒汉式单例实现------面试常问
* */
class LazySingleton{
private static LazySingleton lazySingleton; // 2. 声明一个静态lazySingleton类型的变量
private LazySingleton() {} // 1. 私有构造方法
public static LazySingleton getLazySingleton() { // 3.对外部提供的对象方法内部 做创建对象的操作
if (lazySingleton == null) { // 如果当前lazySingleton 为null ----> 外部访问当前类对象
lazySingleton = new LazySingleton(); // 为当前静态变量 创建一次对象
}
return lazySingleton;
}
}
object类
object类是所有类的基类,也可以称之为万类之祖(顶级父类).
我们创建的所有类,默认都是直接隐式继承Object或间接继承Object. extends Object
为什么要有Object这个类,在Java这块语言设计中,开发Java的人会认为以后所有创建的类都有共性的内容,所以Object类中存放了所有类共有的一些功能.Object提供了一系列基础的结构支撑.
toString方法
toString方法,适用性:当一个类中存在很多成员变量时,而其对象有时需要将对应的数据全部显示出来,如果一个一个都通过对象打点的形式调用,很麻烦.所以可以为当前类重写Object类中的toString方法. 通过打印对象即可显示出对象的所有数据.
toString 方法 一般都是用来打印类的一些数据信息
快捷生成toString方法(试用idea):右键–> 生成–> toString()
之后选择你需要显示的属性即可。然后就会自动重写toString()这个方法了
这里除了可以自动生成toString方法之外,还可以生成get 和set方法 构造函数等等。
代码演示:
在同一个包中先创建一个Point
package objectdemo;
/**
* point 代表点的意思
* */
public class Point {
private int x;
private int y;
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package objectdemo;
/**
* object相关内容使用演示类
* */
public class ObjectDemo {
public static void main(String[] args) {
// 向上造型:一定父子关系,声明父new子
// Object o1 = new Aoo();
Point p = new Point(1,2);
// System.out.println(p.getX());
// System.out.println(p.getY());
// 直接打印对象的话,会自动调用当前对象的toString 方法, 如何本身没有,那么就会调用父类object的toString方法
//p.toString(); //这里的toString方法是object的
// 如果打印对象一定会调用toString 方法的话,当前显示的信息有意义吗? 没有意义
// 例如:当打印对象时,希望显示的是当前对象数据的内容! p----> x 1 y 2
// System.out.println(p); // 打印内容是 当前类的全包名@对象地址 objectdemo.Point@27d6c5e0
System.out.println(p); // 重写toString()方法之后的运行结果:Point{x=1, y=2}
}
}
class Aoo{
}
equals方法
适用性:当若需要判断一个类型中两个对象之间的内容是否相等时,我们可以为当前对象的类型重写equals方法.
默认是判断两个对象之间的地址是否相等。
在Point类中重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
package objectdemo;
/**
* object相关内容使用演示类
* */
public class ObjectDemo {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(1,2);
// Point是引用类型
//判断的是p1 中的内存地址与p2中存储的地址是否相同
System.out.println(p1 == p2); //false
//object 提供equals 是来判断当前调用equals方法的对象与传递进入的对象是否相等
// System.out.println(p1.equals(p2)); //false 这里判断的也是两个对象的地址
// 所以也可以重写一下这个方法
System.out.println(p1.equals(p2)); //true
}
}
class Aoo{
}
在String中的equals是已经重写了的。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
这段代码是 String 类的 equals 方法的实现。它用于比较两个 String 对象是否相等。下面是这个方法的解释:
-
if (this == anObject) { return true; }: 首先,它检查是否 this 对象(调用 equals 方法的对象)和传入的 anObject 对象是同一个对象的引用。如果是,那么它们肯定相等,直接返回 true。
-
return (anObject instanceof String aString): 接下来,它检查 anObject 是否是 String 类型的实例。如果不是,就返回 false。如果是 String 类型的实例,它将 anObject 强制类型转换为 aString。
-
&& (!COMPACT_STRINGS || this.coder == aString.coder): 这个条件主要是用于处理字符串的编码方式,通常是在 Java 9 或更高版本中使用。COMPACT_STRINGS 是一个标志,用于启用或禁用字符串的紧凑表示。如果启用了紧凑表示,它会检查两个字符串的编码方式是否相同,否则,它将忽略这个条件。
-
&& StringLatin1.equals(value, aString.value): 最后,它调用 StringLatin1.equals 方法来比较两个字符串的值。这个方法会比较字符串的字符是否相同。
面试题:
== 和 equals 的区别?
答: == 是关系运算符, equals是一个方法,结果都是返回boolean值。
Object类中的equals方法 实际上比较的还是地址是否相同,因为用的还是==.(在object类中还是 ==,所以还是需要进行重写)
==作用 :在基本数据类型中,比较的是值是否相等。
在引用数据类型中,比较的是地址是否相等。
equals方法的作用:
JDK中一般常用的类基本上都已经重写实现了equals方法,比较的是内容。
自定义的类,如果没有重写equals方法,将会默认调用父类的equals方法。
我们可以按照实际的比较逻辑,重写equals方法。
包装类:
Java提供了基本类型对应的8个包装类。
包装类:基本数据类型都是以值而非对象的形式存在,因此没有面向对象特性,但是为了让基本数据类型也参与到面向对象的开发中,从而设计出对应的8个包装类,目的就是可以让基本数据类型可以以对象的形式存在。
基本数据类型转换为对应包装类类型
package wrapdemo;
/**
* 包装类的使用演示类:
* */
public class WrapClassDemo {
public static void main(String[] args) {
/**
* 基本数据类型 包装类
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* boolean Boolean
* char Character
* */
// 将基本数据类型 转化为对应的包装类类型!
int number = 100; //int数据类型
// Integer i1 = new Integer(number);
// Integer i2 = new Integer(number); //将基本数据类型的数据 存储给 类类型
// System.out.println(i1);
// System.out.println(i2);
// System.out.println(i1 == i2); //false 比较的还是地址 存储的还是地址
// System.out.println(i1.equals(i2)); //true 在Integer类中已经重写过equals
//在java9 上面这种方式就被弃用了,现在推荐使用下面的写法
//int 类型转换为Integer
Integer i1 = Integer.valueOf(number); //将基本数据类型的数据 存储给 类类型
// 这里的数据如果是在(-128 ~ 127)那么就可以做缓存,缓存的意思和常量池相似(复用)
Integer i2 = Integer.valueOf(100); //可以缓存一个字节的数据 (-128 ~ 127)
System.out.println(i1);
System.out.println(i2);
System.out.println(i1 == i2); //true 这里实现了复用,共用一个内存地址
// 如果数据超过一个字节那么就无效了
//********************************************
// 这里所有的基本数据类型使用都是一样的
byte b = 100;
Byte b1 = Byte.valueOf(b);
//--------------------------------------
double d = 1.23;
Double d1 = Double.valueOf(d); //valueOf本质上就是new对象 (推荐使用这种方式)
Double d2 = new Double(d); //这里两种方式都是一样的 double是没有缓存的
System.out.println(d1); //1.23
System.out.println(d2); //1.23 所以在Double中也重写了toString方法
System.out.println(d1.equals(d2)); //true
Integer I = Integer.valueOf(100);
}
}
将包装类中的内容转换为对应的基本数据类型来存储
package wrapdemo;
/**
* 包装类的使用演示类:
* */
public class WrapClassDemo {
public static void main(String[] args) {
/**
* 基本数据类型 包装类
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* boolean Boolean
* char Character
* */
// 将包装类中的内容转换为对应的基本数据类型来存储
Integer I = Integer.valueOf(100);
int n1 = I.intValue();
System.out.println(n1);
//-------------------
Double d3 = Double.valueOf(2.22);
double dd = d3.doubleValue();
System.out.println(dd); // 2.22
//转为int
int id = (int)d3.doubleValue();
System.out.println(id); //2
}
}
包装类中的常量和包装类将字符串内容转换为对应的基本数据类型
package wrapdemo;
/**
* 包装类的使用演示类:
* */
public class WrapClassDemo {
public static void main(String[] args) {
/**
* 基本数据类型 包装类
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* boolean Boolean
* char Character
* */
//---------------------------------
// 包装类有两个常量,分别对应的是 获取当前类型可容的最大值 和最小值
System.out.println(Integer.MAX_VALUE); //2147483647
System.out.println(Integer.MIN_VALUE); //-2147483648
System.out.println(Byte.MAX_VALUE); //127
//包装类还可以将字符串的内容,转换为对应的基本类型 前提是字符串中的数据正确
// 这里你使用的字符串如果是整数那么就使用Integer,如果是小数的,那么就使用Double
String line = "123";
int l1 = Integer.parseInt(line); //这里必须是整数
System.out.println(l1); //123
line = "123.123";
double dd1 = Double.parseDouble(line);
System.out.println(dd1); //123.123
}
}
自动拆装箱
JDK5以后,推出了一个新特性,自动拆装箱。
编译器如果遇到基本数据类型 和引用类型相互赋值的情况时,会自动补充完成他们之间的转换代码。特性只是编译器认可,字节码的文件中,并不会存在互相之间可以赋值的情况。
装箱:指的是将基本数据类型 装载到 引用数据类型的一个过程
拆箱:指的是将引用类型的数据装载到 基本数据类型的一个过程.
package wrapdemo;
/**
* 拆装箱的使用演示类:
* */
public class WrapDemo02 {
public static void main(String[] args) {
/**
* 装箱:指的是将基本数据类型 装载到 引用数据类型的一个过程
*
* 在字节码中实际写法 ---指的就是装箱的过程
* Integer integer = Integer.valueOf(i)
* */
int i= 123; //基本数据类型
Integer integer = i; // 编译器支持
/**
* 拆箱:指的是将引用类型的数据装载到 基本数据类型的一个过程.
*
* 在字节码中实际写法 --- 指的就是拆箱的过程
* i = integer.intValue();
* */
i = integer; //编译器支持
}
}
面试题:
什么是拆箱?什么是装箱? 拆装箱的执行过程?
答: 装箱:指的是将基本数据类型 装载到 引用数据类型的一个过程
装箱执行过程是调用包装类的valueOf方法来实现的.
拆箱:指的是将引用类型的数据装载到 基本数据类型的一个过程.
拆箱执行过程是调用包装类中的xxValue方法来实现的.
注意:算数运算符会自动触发拆装箱。
Integer i1 = 0;
i1 += 10; // 算术运算符会自动触发拆装箱, 拆装箱是会消耗内存的,
int i2 = 0;
i2 += 10; // 这里的效率是比拆装箱好
冒泡排序:
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
运行图:
执行过程:
package wrapdemo;
import java.util.Arrays;
public class BubbleSortDemo {
public static void main(String[] args) {
// 排序:指的是在一个数组中,将数据进行从小到大排序(升序),或将数据进行从大到小排序(降序)
/**
* 30 50 80 90 20 10 ------- 一组数据
* //1.6个数据比较 需要比较几轮 2.每轮要比多少次
* 第一轮
* 30 50 80 20 10 90 ------- 第一轮6个数据比较 比较了5次
* ① ② ③ ④ ⑤
* 第二轮
* 30 50 20 10 80 ------- 第二轮5个数据比较 比较了4次
* ① ② ③ ④
* 第三轮
* 30 20 10 50 ------- 第三轮4个数据比较 比较了3次
* ① ② ③
* 第四轮
* 20 10 30 -------第四轮3个数据比较 比较了2次
* ① ②
* 第五轮 -------第五轮2个数据比较 比较了1次
* 10 20
* ①
*
* 顺序: 10 ,20,30,50,80,90 从小到大排序
*/
//双重for循环来实现冒泡排序
/**实现规则:
* 1. 6个数 比5 轮
* 2. 每一轮都是从第一个元素开始比
* 3. 每一次都是和下一个元素比
* 4. 每一轮经过后,已经比较出当前轮最大元素 不再参与下次比较.
*/
int[] a = {30, 50, 80, 90, 20, 10};
for (int i = 0; i <a.length -1 ; i++) {
for (int j = 0; j < a.length -1 - i; j++) {
if (a[j] > a[j+1]) {
int temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
/** array.length - 1 - i
* 第一轮 i 为 0 比较 5 次
* 第二轮 i 为 1 比较 4 次
* 第三轮 i 为 2 比较 3 次
* 第四轮 i 为 3 比较 2 次
* 第五轮 i 为 4 比较 1 次
*/
System.out.println(Arrays.toString(a));
}
}