2.3.3 引用数据类型
引用数据类型大致包括:类、 接口、 数组、 枚举、 注解、 字符串等
它和基本数据类型的最大区别就是:
- 基本数据类型是直接保存在栈中的
- 引用数据类型在栈中保存的是一个地址引用,这个地址指向的是其在堆内存中的实际位置。(栈中保存的是一个地址,而实际的内容是在堆中,通过地址去找它实际存放的位置)
1、String字符串
在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串,所以String是引用数据类型。Java语言中字符串必须包含在一个 " " 中。
字符串的创建:
两种创建方式:
1、String str1 = "name"; //直接定义,在常量池中
2、String str2 = new String("name"); //在常量池创建后,再在堆中创建对象
//new每次都会创建一个新的对象,而String直接创建的字符串如果值相同则每次指向的都是同一个对象
//String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了,
//重新赋值只是改变存储的数据地址
1和2两种创建方式的区别:
两者都会去字符常量池中检查是否存在 "name",如果有则直接引用,没有则在常量池中创建(其实这个实例也是在堆中的,字符串常量池中是引用地址)
但是2还会通过new在堆中创建一个同样内容的对象实例
String是最基本的实际类型吗?
不是,基本数据类型有:byte、short、int、long、float、double、Boolean、char8种
ava.lang.String类是final类型的,所以String不能继承和修改。String、StringBuffer、StringBuilder的区别
都是用于操作字符串,但是String类是final类型,内容不可改。StringBuffer、StringBuilder类可修改。String的更改是从新创建一个对象,再指向这个新的对象,所以大量操作时速度较慢。StringBuffer、StringBuilder可直接修改值。
StringBuilder的运行效率高,但是线程不安全。如果字符串变量在方法中定义,只能有一个线程访问的情况下使用
StringBuffer是线程安全的,在类里定义成员变量,且实例对象在多线程环境中使用,则用此类
常用的方法
1、字符串的拼接:
用'+'来拼接多個字符串
String a = "hello"; String b = "Java"; System.out.println(a+b);
結果:hello Java
用'+'连接字符串和其他数据类型
String a = "3+2="; int b = 5; System.out.println(a+b);
结果:3+2=5
str1.concat(str2):会在str1后拼接str2
String str1 = "拼接";
String str2 = "一起";
System.out.println(str1.concat(str2)); //结果:拼接一起
System.out.println(str1); //结果:拼接
System.out.println(str2); //结果:一起
2、获取字符串长度:
str.length();
3、获取字符串中的字符:
str.charAt(i); //i是字符串内每个字符的索引位置从0开始,length-1结束
4、比较字符串
不忽略字母大小写:str1.equals(str2);
忽略字母大小写:str.equalsIgnoreCase(str2);
5、获取某一字符的位置:
查找指定字符第一次出现的位置:str.indexOf("指定的字符")
从指定位置(int)开始查找指定字符第一次出现的位置:str.indexOf("指定的字符",int)
查找指定字符最后一次出现的位置:str.lastIndexOf("指定的字符")
从指定位置(int)开始往前查找指定字符第一次出现的位置:str.lastIndexOf("指定的字符",int);
6、字符串的截取:
从指定位置截取到最后:str.subString(int)
从指定位置截取到指定位置:str.subString(int1,int2); 区间[int1,int2)
7、去除空格:
去除首位空格:str.trim()
去除所有空格(将所有空格替换):str.replaceAll("//s","");
8、替换字符串中指定的字符
将所有符合指定的字符串都替换成新字符串:str.replaceAll("原字符串","新字符串") 可使用字符串格式
将指定字符替换换成其他字符:str.replace('原字符','新字符') 替换单个字符
将符合指定字符串的第一个替换成新字符串,后续的不变:str.replaceFrist("原字符串","新字符串");
9、判断字符串的开头与结尾(返回的是布尔值):
判断某字符串是否以**开头:str.startsWith("**")
判断字符串从指定位置开始是否以**开头:str.startsWith("**",int)
判断某字符串是否以**结尾:str.endsWith("**");
10、字符串中大小写转换:
大写变小写:str.toLowerCase()
小写变大写:str.toUpperCase();
11、分割字符串:
以指定字符分割:str.split('分隔符')
以指定字符分割指定次数:str.split('分隔符',int);
12、判断是否为空字符串:
str.isEmpty();
13、字符串的格式化:
String str = String.formart("",); //和格式化打印类似
+可以用来连接字符串,也可以进行运算,所以在使用时需要注意:
2、数组
数组是固定长度、存储相同数据类型的一种数据结构,在内存中的体现是一个连续的内存空间。有索引值,可通过索引快速快速查询、修改相应元素。
基本特点:
1、数组的长度定义后不可更改,数组是有界限的[0,length-1],所以在使用过程中经常会遇到数组下标越界的异常
2、一个数组的类型定义后不可改,内部保存的元素必须是相同的
3、数组可定义为任意数据类型类型
4、数组属于引用类型,存放在堆中,栈中的数据实际上保存的是对象在堆中的地址
5、数组的访问通过索引值
Arrays类:
位于 java.util 包中,主要包含了操纵数组的各种方法
常用的函数:
1、打印数组元素 toString、deepToString
Arrays.toString(数组名); //一维数组
Arrays.deepToString(数组名); //多维数组
2、数组元素升序排列:sort
Arrays.sort(数组名); //数值按大小排序,String类型数组按照字典顺序,数字、大写字母、小写字母汉字的顺序
3、填充值:fill
Arrays.fill(数组名,值); //用指定的值填充数组全部的元素
Arrays.fill(数组名,start,end,值); //数组下标为[start,end)的元素全部填充为指定值
4、比较数组:equals
Arrays.equals(数组1,数组2);
5、复制数组 copyOf、copyOfRange
copyOf(数组名,复制的长度); //超过要复制的数组长度时,整型用0,char用null,小于数组长度截取数组
copyOfRange(数组,start,end); //[start,end)截取数组进行复制
6、数组查询 binarySearch
binarySearch(数组,值) //如果要搜索的值在数组内,则返回该值的索引,否则返回-1
binarySearch(数组,start,end,值) //在[start,end)范围内搜索相应的值
冒泡排序:
最常用的数组排序算法之一,将数组元素总是将小数往前放,大数往后放。基本思想是对比相邻的元素值,按规律进行交换。
public class BubbleSort {
public static void main(String[] args) {
int arr[] = {26,15,29,66,99,88,36,77,111,1,6,8,8};
for(int i=0;i < arr.length-1;i++) {//外层循环控制排序趟数
boolean flag = false; //通过布尔值进行判断,可减少一定的循环次数
for(int j=0; j< arr.length-i-1;j++) {//内层循环控制每一趟排序多少次
// 把小的值交换到前面
if (arr[j]>arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = true; //如果判断过,flag就为true
}
}
if(!flag){
break;
}
}
System.out.println("最终排序结果:");
for(int a = 0; a < arr.length;a++) {
System.out.println(arr[a] + "\t");
}
}
}
3、 枚举
枚举类型是Java 5中新增特性的一部分,它既是一种类(class,内部可以有方法和属性)却又比类多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
-
使用的场景
-
需要定义一组常量的时候:一年4个季节、一年12个月份、一星期7天、男女性别、支付方式等等。
-
创建单例模式的时候,内部只有一个对象,最简单的单例模式创建的方式
-
-
使用规则
- 不能被继承
- 不能被单独的new创建对象
- 枚举成员是用
,
隔开的。
在定义枚举类型时我们使用的关键字是enum,enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。有以下方法:
- values() 返回枚举类中所有的值。
- ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
- valueOf()方法返回指定字符串值的枚举常量。
//枚举类型,使用关键字enum,定义周一到周日的常量
public enum Week {
//不使用枚举需要定义常量
// private final static String MONDAY = "星期一";.......
MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"),
THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星六"),
SUNDAY("星期天");
//枚举可以作为一个类,添加属性和方法
Week(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Week week1 = Week.FRIDAY;
//在switch中使用
switch (week1){
case MONDAY: case TUESDAY: case WEDNESDAY:
case THURSDAY: case FRIDAY: case SATURDAY:
case SUNDAY:
System.out.println(week1.getName());
break;
default:
System.out.println("错误!!");
}
System.out.println("-----------分割线-------------");
//迭代
for (Week week2:Week.values()) {
System.out.println(week2.getName());
}
System.out.println("-----------分割线-------------");
//valueOf 通过指定字符串值返回枚举常量。
Week week3 = Week.valueOf("MONDAY");
System.out.println(week3.getName());
System.out.println("-----------分割线-------------");
//ordinal返回索引位置
Week week = Week.FRIDAY;
int ordinal = week.ordinal();
System.out.println(ordinal);
}