JavaEE基础知识

一、包装类

包装类:基本数据类型对应的引用数据类型

出现原因:Java为纯面向对象语言,但是8种基本数据类型不能new对象,破坏了Java为纯面向对象语言的特征。所以Java为每种基本数据类型匹配了对应的类,这种类叫做包装类/封装类

* 基本数据类型		引用数据类型
* byte					Byte
* short					Short
* int					Integer
* long					Long
* float					Float
* double				Double
* char					Character
* boolean				Boolean
//装箱:把基本数据类型 转换为 对应的包装类对象
Integer integer = Integer.valueOf(10);
System.out.println(integer);

//拆箱:把包装类的对象 转换为 对应的基本数据类型
int i = integer.intValue();
System.out.println(i);

//自动装箱:把基本数据类型 转换为 对应的包装类对象
Integer integer = 10;
System.out.println(integer);

//自动拆箱:把包装类的对象 转换为 对应的基本数据类型
int i = integer;
System.out.println(i);

注意:自动拆装箱是JDK1.5开始的新特性

需求:把该字符串数组转换为int数组

String[] ss = {"1","2","3","4","5","6"};
		
int[] is = new int[ss.length];

for (int i = 0; i < ss.length; i++) {
    //获取出单个的字符串
    String str = ss[i];	
    //转换为int类型
    int num = Integer.parseInt(str);
    //赋值
    is[i] = num;
}

for (int e : is) {
	System.out.println(e);
}

经典面试题

Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);
System.out.println(i1 == i2);//true

Integer i3 = Integer.valueOf(200);
Integer i4 = Integer.valueOf(200);
System.out.println(i3 == i4);//false 底层是-127~128的缓冲数组

深入Integer原理

public class MyInteger {

    private int value;

    public MyInteger(int value) {
    	this.value = value;
    }

    public static MyInteger valueOf(int i){
        if(i >= MyIntegerCache.low && i <= MyIntegerCache.high){
        	return MyIntegerCache.cache[i + -MyIntegerCache.low];
        }
        return new MyInteger(i);
    }

    @Override
    public String toString() {
    	return String.valueOf(value);//将int类型转换为String类型
    }

    //MyInteger的缓冲类
    private static class MyIntegerCache {
        static final int low = -128;
        static final int high = 127;
        static final MyInteger cache[];//缓冲数组:存放的是-128~127的MyInteger对象

        static{
            cache = new MyInteger[(high - low) +  + 1];
            int value = low;
            for (int i = 0; i < cache.length; i++) {
            	cache[i] = new MyInteger(value++);
        	}
    	}
   }

}

二、String

String被声明为final,因此它不可被继承

  • Java8中,String内部使用char数组存储数据
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}
  • Java9中,String类的实现改为byte数组存储字符串,并用coder标识使用的编码
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final byte[] value;

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}

String创建对象问题

//"abc"常量池中只有一份
//有1个String的对象
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);//true

//new了两次,“abc”在常量池中
//有3个String的对象
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println(str3 == str4);//false
---------------------------------------------------------------

String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);//true

//"ab"字面值常量
//"c"字面值常量
//在编译时,就直接拼接为"abc"
String str3 = "ab" + "c";
System.out.println(str1 == str3);//true

//s1 常量
//s2 常量
//在编译时,就直接拼接为"abc"
final String s1 = "ab";
final String s2 = "c";
String str4 = s1 + s2;
System.out.println(str1 == str4);//true

//s3 和 s4 中有一个是常量 或者 都是变量的情况下
final String s3 = "ab";
String s4 = "c";
String str5 = s3 + s4;//new StringBuilder(s3).append(s4).toString();
System.out.println(str1 == str5);

总结:
两个常量字符串拼接,在编译时就直接拼接
其中有一个是变量的情况,底层会创建一个StringBuilder的容器进行存储

String类的常用方法


concat(String str)         在字符串末尾追加内容,并返回新的字符串

substring(int beginIndex)     从开始下标处截取到字符串末尾,并返回新的字符串

substring(int beginIndex, int endIndex)    从开始下标处(包含)截取到结束下标处(不包含),并返回新的字符串

trim()     去掉首尾空格,并返回新的字符串

replace(char oldChar, char newChar)     替换字符,并返回新的字符串

replaceAll(String regex, String replacement)    替换字符串,并返回新的字符串

replaceFirst(String regex, String replacement)       替换第一个出现的字符串($$报错)

length()       获取字符串长度

str1  ==   str2       比较内存地址

equals(Object anObject)     比较两个字符串内容是否相等(区分大小写)

equalsIgnoreCase(String anotherString)      比较内容是否相同(不区分大小写)

startsWith(String prefix)     判断该字符串是否以某个字符串开头

endsWith(String suffix)      判断是否以某个字符串结尾

indexOf(int ch)     查询ASCII在字符串中第一次的下标

indexOf(String str)     查询字符串在字符串中第一次的下标

lastIndexOf(int ch)     查询ASCII在字符串中最后一次的下标

lastIndexOf(String str)      查询字符串在字符串中最后一次的下标

charAt(int index)      获取指定下标上的字符
String string = "123abcABC123";
string = string.concat("OOO");// 末尾添加
string = string.substring(3);// 从下标3开始截取
string = string.substring(3, 6);// 截取下标3~6(不包括)
String string1 = "   123    abc  A B C  123   ";
string1 = string1.trim();// 去掉首尾的空格
string1 = string1.replace('2', '-');// 替换字符
string1 = string1.replaceAll(" ", "");// 替换字符串
string1 = string1.replaceFirst("3", "@@");// 替换第一个出现的字符串($$报错)
System.out.println(string1);
System.out.println(string1.length());// 获取长度
System.out.println("比较内存地址" + (string == string1));
System.out.println("比较值是否相同(区分大小写)" + string.equals(string1));
System.out.println("比较内容是否相同(不区分大小写)" + string.equalsIgnoreCase(string1));
System.out.println("判断是否以某个字符串开头" + string1.startsWith("1-"));
System.out.println("判断是否以某个字符串结尾" + string1.endsWith("1-3"));
System.out.println("查询ASCII在字符串中第一次的下标" + string1.indexOf(97));
System.out.println("查询字符串在字符串中第一次的下标" + string1.indexOf("a"));
System.out.println("查询ASCII在字符串中最后一次的下标" + string1.lastIndexOf(97));
System.out.println("查询字符串在字符串中最后一次的下标" + string1.lastIndexOf("a"));
System.out.println("获取指定下标的字符" + string1.charAt(4));
char[] charArray = string1.toCharArray();// 获取字符数组
System.out.println(charArray);

练习:完成一个邮箱格式的校验 tc@qq.com
(1)“@”不能在第一位
(2)“.”不能在最后一位
(3)“@”和“.”中间应该有字符
(4)***@***.***
当然还有更严格的校验规则,我们此处考虑到这即可

String email = "tc@qq.com";
		
int len = email.length();
int index1 = email.indexOf("@");
int index2 = email.indexOf(".");

if(index1 == 0 || index1 == len-1 || index2 == 0 || index2 == len-1 || (index2-index1) < 2){
	System.out.println("邮箱格式不正确");
}

三、StringBuffer

含义:StringBuffer代表可变的字符序列

StringBuffer称为字符串缓冲区,它的工作原理是:预先申请一块内存,存放字符序列,
如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。
StringBuffer是可变对象,这个是String最大的不同

//默认缓冲区大小16个字符
//StringBuffer sb = new StringBuffer();

//设置指定缓冲区大小
//StringBuffer sb = new StringBuffer(30);

//设置缓冲区大小:"123abcDEF".length() + 16
StringBuffer sb = new StringBuffer("123abcDEF");

sb.append("123");//在末尾追加内容
sb.insert(3, "xxx");//在指定下标上插入字符串
sb.setCharAt(4, '-');//替换指定下标上的字符
sb.replace(3, 6, "xxxyyy");//从开始下标处(包含)替换到结束下标处(不包含)
sb.deleteCharAt(10);//删除指定下标上的字符
sb.delete(3, 9);从开始下标处(包含)删除到结束下标处(不包含)
sb.reverse();//反转字符

System.out.println("获取字符长度:" + sb.length());

System.out.println(sb);//321FEDca321

四、StringBuilder

用法和StringBuffer一样

五、String,StringBuffer,StringBuilder区别

类别可变性线程安全性效率
String不可变线程安全
StringBuffer可变线程安全
StringBuilder可变线程不安全

(1)可变性

  • String的不可变性
  • StringBuffer和StringBuilder都继承自AbstractStringBuilder类,AbstractStringBuilder使用char数组来保存字符串,是可变的。

(2)线程安全性

  • String的不可变性保证了线程安全
  • StringBuilder线程不安全
  • StringBuffer对方法加入了synchronized同步锁进行同步,因此线程安全

(3)效率

​ StringBuilder > StringBuffer > String

  • 由于String的不可变性,导致每次对String的改变都会生成新的对象,因此在大量的字符串操作下,会造成效率低下,且大量消耗内存
  • StringBuffer和StringBuilder会通过append方法,将新的字符串加入value数组末尾(默认value数组的容量为16)
  • StringBuffer对方法加入了同步锁,因此效率低于StringBuilder,但在多线程环境下,不应该使用StringBuilder

六、String Pool 字符串常量池

在JVM中存放着一个字符串池,其中保存着很多String对象,这些对象可以被共享使用。当以字符串直接创建String对象时,会首先在字符串池中查找是否存在该常量。如果不存在,则在String Pool中创建一个,然后将其地址返回。如果在String Pool中查询到已经存在该常量,则不创建对象,直接返回这个对象地址。

字符串常量池存在于运行时常量池中

常量池常分为两大类:
(1)静态常量池:用于存放编译后生成的各种字面量和符号引用。
(2)运行时常量池:再类加载后产生的新常量放入运行时常量池中。运行时常量池主要包含:

  • 类、接口、方法、类字段表述信息
  • 字符串常量池
  • 被final修饰的类变量
  • 包装类缓存池

Tips:

  • JDK1.7之前,运行时常量池逻辑包含字符串常量池,放在方法区,此时hotspot虚拟机对方法区的实现为永久代。
  • JDK1.7字符串常量池从方法区拿到了堆中,原因是方法区空间有限,在大量使用字符串的场景下,会导致内存溢出(OutOfMemory)
  • JDK1.8 hotspot用元空间替换了永久代,字符串常量池还在堆中,方法区的实现从永久代变为元空间。

字面量创建字符串常量:String s = "abc"

使用字面量创建字符串常量时(String s = “abc”),JVM首先会检查 String Pool 字符串常量池中是否存在"abc"字符串对象,若该字符串对象已存在于 String Pool 中,则会直接将常量池中该对象的内存地址赋值给 s。

String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6);  // true
123

使用关键字new创建字符串常量:String s = new String(“abc”)

使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。

  • 由于"abc"作为构造参数,本身就是一个字面量字符串,所以若String Pool中没有 "abc"字符串对象,那么就会在在 String Pool 中创建一个字符串对象。
  • 使用new关键字会在堆中创建一个字符串对象。
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);           // false
123

String.intern()解析

intern方法: 如果String Pool 中已经存在该字符串对象,则直接返回String Pool中该对象的引用。否则,在String Pool中加入该对象,然后返回引用。(这里的加入该对象,在Java7之前和Java7及以后的处理方式不同)

String s1 = new String("1"); // String Pool中和堆中分别创建了一个对象,s1指向堆中的对象
s1.intern();	 //String Pool中已经创建过该字符串对象了
String s2 = "1"; //s2指向String Pool中的字符串对象
System.out.println(s1 == s2);  //false
1234

JDK1.6与JDK1.7 intern方法的区别

String s3 = new String("1") + new String("1"); // 堆中有 "1"和"11"两个对象,String Pool中有"1"一个对象,s3指向堆中的'11"对象
s3.intern();  //将“11”对象加入String Pool中,JDK1.6 和 JDK1.7不同
String s4 = "11"; 
System.out.println(s3 == s4); // JDK1.6:false   JDK1.7:true
1234

JDK1.6:调用s3.intern();若字符串常量池中不存在该字符串对象,则在String Pool中创建一个对象,然后返回它在String Pool的内存地址;若存在则直接返回String Pool中该对象的内存地址。

JDK1.7:调用s3.intern();若字符串常量池中不存在该字符串对象,则会将堆中此对象的引用添加到String Pool中,并返回该引用;若存在则直接返回String Pool中该对象的内存地址。

Tip:也就是说JDK1.7开始,String Pool中即可以存放字符串对象,也可以存放字符串对象的引用。因此JDK1.6中 String Pool存放的是"11"字符串对象,s4就指向String Pool中的"11"对象,因此是false,而JDK1.7 String Pool 存放的是"11"字符串对象在堆内存的引用,所以s4和s3是同一个对象,结果为true。

String里的"+"操作

  • 字面量常量字符串之间的"+"操作会在编译阶段优化,直接合成一个字符串。
    如:String str = “JA”+“VA”; 编译后等同于String str = “JAVA”;因此String Pool中只会有"JAVA"这一个对象。
  • 字面量常量字符串与变量的"+“操作,会调用StringBuilder.append()在堆中创建新的对象。
    如:String str2 = str + “01”; 等同于 String str2 = new StringBuilder(str).append(“01”);会将str转化成一个StringBuilder对象,然后调用append方法,添加"01”,因此str2不会进入常量池,但由于每次拼接字符串时都会new StringBuilder,所以在循环拼接字符串时,不建议用 + 号。
  • 被final修饰的字段,编译期直接进行常量替换
fianl Sring s1 = "Ja";
fianl Sring s2 = "va";
String s3 = s1 + s2;
123

在编译时,直接替换成:String s3 = “Ja” + “va”; 又由于 “Ja” 和 “va” 都是字面量常量字符串,于是又被优化成:String s3 = “Java”;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值