Java 基础知识点 笔记总结

Posted IT_Holmes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 基础知识点 笔记总结 相关的知识,希望对你有一定的参考价值。

1. String 特性

  • String声明为final的,不可被继承。
  • String实现Serializable接口:表示字符串是支持序列化的。实现了Comparable接口:表示String可以比较大小。
  • String内部定义了final char[] value 用于存储字符串数据。
  • String:代表不可变的字符序列。简称:不可变性。


(注意:上面的final,如果重新赋值,并且内容不同,就是重新获得新的地址,并不会在原来的地址上赋值!!!!)


面试题:== 和 equals的区别?

  • 比较s1和s2的地址是否相同。
  • 比较字符串内容值是否相同。
package com.holmes.java04;

import org.junit.Test;

public class StringTest {
    @Test
    public void test1(){
        String s1 = "abc";
        String s2 = "abc";

        System.out.println(s1 == s2);//比较s1和s2的地址是否相同。
        System.out.println(s1.equals(s2));//比较字符串内容值是否相同。
    }
}

String字符串,什么情况下,重新指定内存区域进行赋值:

无论,重复赋值,还是拼接字符串,还是修改指定替换字符串等,都是要重新指定内存区域进行赋值!!!

2. String的字面量创建 和 new 对象形式


String对象创建的四种方式:

整体上,也就两种方式:一种字面量,一种new创建对象。

  • 通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
  • 通过 new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对象的值。
package com.holmes.java04;

import org.junit.Test;

public class StringTest {
    @Test
    public void test1(){
        //通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
        String s1 = "javaEE";
        String s2 = "javaEE";

        //通过 new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对象的值。
        String s3 = new String("javaEE");
        String s4 = new String("javaEE");

        System.out.println(s1 == s2);//true
        System.out.println(s1 == s3);//false
        System.out.println(s3 == s4);//false
    }
}

3. String 字符串 内存存储原理


字面形式和new + 对象的内存存储原理:

面试题:String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?

两个,一个是堆空间中new的对象,另一个是char[]对应常量池中的数据:“abc”。

4. String 不同拼接对比 效果

下面的拼接效果必须牢记!!!!

package com.holmes.java04;

import org.junit.Test;

public class StringTest {
    @Test
    public void test1(){
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE"+"hadoop";

        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;
        String s8 = (s1 + s2).intern();

        //没有变量相加,仅仅只是字符串,这样声明的s4的内存地址就仅仅在常量池中。
        System.out.println(s3 == s4); //true

        //只要有变量相加的,那么该创建方式就是new对象形式,也就是要存储到堆内存地址。
        System.out.println(s3 == s5); //false
        System.out.println(s3 == s6); //false
        System.out.println(s5 == s6); //false
        System.out.println(s3 == s7); //false
        System.out.println(s5 == s7); //false
        System.out.println(s6 == s7); //false

        //通过调用intern()方法,返回值就在常量池中!
        System.out.println(s3 == s8); //true
    }
}

5. String 和 数组 常用的混合面试题

package com.holmes.java04;

public class StringTest2 {

    //也算涉及到了String类型的不可变性。
    //这里与下面change函数中的str的内存地址不同!
    String str = new String("good");
    char[] ch = {'t','e','s','t'};

    public void change(String str,char ch[]){

        //这里的str与上面的str不同!!地址不同,这里是形参是参数!!!
        str = "test ok";
        ch[0] = 'b';

    }

    public static void main(String[] args) {

        StringTest2 ex = new StringTest2();
        ex.change(ex.str,ex.ch);

        //有人疑惑这里为什么不是test ok, 原因很简单,因为这里的ex.str是作为形参传入。
        System.out.println(ex.str); //good

        //至于数组没有不可变形,对应地址相同,只不过内容中的第一个值变化了而已。
        System.out.println(ex.ch); //best
    }
}

6. JVM涉及字符串 内存结构


JDK1.6版本,字符串常量池都在方法区中:


JDK除了1.7版本,字符串常量池在堆中:


JDK1.8以及以后的版本,字符串常量池都在方法区中:

7. String 常用方法



问题一:什么情况下,indexOf(str) 和 lastIndexOf(str)返回值相同?

答:情况一:存在唯一的一个str。情况二:不存在str时。
此外,indexOf 和 lastIndexOf方法如果未找到都是返回-1。


上面的replaceAll()函数方法,matches()函数方法,涉及到正则表达式内容,如下:

8. 涉及到String 类与其他结构之间的转换

8.1 常见的小错误


首先,纠正一个经常犯下的错误:

String str = "123";
//错误的,强转的前提必须是子父类的前提下!!!
int num = (int)str; 

8.2 String 和 int 之间的转换


String 转 int:(调用包装类的静态方法,Integer.parseXxx())

String str = "123";
//调用包装类的静态方法,才是正确的
int num = Integer.parseInt(str)

int 转 String:(调用包装类的静态方法,Integer.toString() 和 String.valueOf())

String s2;
String s3;
int num2 = 123;
//方式一:Integer.toString()方法
s2 = Integer.toString(num2);
//方式二:String.valueOf()方法
s3 = String.valueOf(num2);

System.out.println(s2.getClass()+ "," +s3.getClass());
//class java.lang.String , class java.lang.String

//方式三:就是通过加空字符串 "" 。
String s4 = 123 + "";

8.3 String 和 char[]字符组 之间的转换

牢记toCharArray()函数方法:

package com.holmes.java04;

import org.junit.Test;

public class StringTest {
    @Test
    public void test1(){

        String str1 = "abc123";

        //调用String的toCharArray()拆分字符串 转为 字符数组。
        char[] charArray = str1.toCharArray();
        //遍历一下charArray
        for (int i=0;i<charArray.length;i++){
            System.out.println(charArray[i]);
        }

        char[] arr = new char[]{'h','e','l','l','o'};
        //直接将字符数组当作参数放入字符串对象,就直接形成了字符串
        String str2 = new String(arr);
        System.out.println(str2);


    }
}

8.4 String字符串 和 byte[]字节数组 之间的转换

这里就会出现一个编码和解码的一个效果:


String 转为 byte[]:
(调用String的getBytes()方法)

package com.holmes.java04;

import org.junit.Test;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class StringTest {
    @Test
    public void test1() throws UnsupportedEncodingException {

        //调用String的getBytes()方法
        String str1 = "abc123中国";
        byte[] bytes = str1.getBytes(); //使用默认的字符集(utf-8),进行转换
        //Arrays.toString()方法显示字符串。
        System.out.println(Arrays.toString(bytes));
        //[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67],这里结果就是aciII码
        //此外,在utf-8当中一个汉字是占3个字节。

        //使用gbk编码,就要添加参数
        byte[] gbks = str1.getBytes("gbk");
        System.out.println(Arrays.toString(gbks));
        //[97, 98, 99, 49, 50, 51, -42, -48, -71, -6] 在gbk中一个汉字包含两个字节。


    }
}

byte[]字节数组 转 String字符串:
(new对象参数传入,来转换)

package com.holmes.java04;

import org.junit.Test;

import java.io.UnsupportedEncodingException;

public class StringTest {
    @Test
    public void test1() throws UnsupportedEncodingException {

        //调用String的getBytes()方法
        String str1 = "abc123中国";
        byte[] bytes = str1.getBytes(); //使用默认的字符集(utf-8),进行转换
        byte[] gbks = str1.getBytes("gbk");

        //同样这里也是用默认的字符集(utf-8)来进行解码
        String str2 = new String(bytes);
        System.out.println(str2);

        //因为这里是通过gbk编码的,但使用utf-8来解码就会乱码
        String str3 = new String(gbks);
        System.out.println(str3);

        //abc123中国
        //abc123�й�
    }
}

9. String 拼接 小面试题

因为一个final,就导致结果不同,牢记常量与常量拼接的结果是存储在常量池中,且常量池不会存在相同内容的常量。final声明后变量就变成了常量!

package com.holmes.java04;

import org.junit.Test;

public class StringTest {
    @Test
    public void test1(){
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3); //false

        //final声明后,s4就变成了常量,常量和常量的拼接结果就在常量池中,因此返回true。
        final String s4 = "javaEE";
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5); //true
    }
}

10. StringBuffer 和 StringBuilder 介绍

本质上字符串存储规则,其实就是个char[]数组,String不可变,StringBuffer和StringBuilder可变。


String , StringBuffer , StringBuilder三者的异同?

String:不可变的字符序列。
StringBuffer:可变的字符序列:线程安全的,效率偏低。
StringBuilder:可变的字符序列:jdk5.0新增,线程不安全的,效率高。

上面三者底层使用char[]存储。(StringBuffer和StringBuilder因为继承AbstractStringBuilder,因此也是char[] value存储。)


11. String ,StringBuffer,StringBuilder 源码分析


String源码分析:

String str = new String(); 
//执行实际源码为:char[] value = new char[0];

String str1 = new String("abc");
//执行实际源码为:char[] value = new char[]{'a','b','c'};

StringBuffer源码分析:(StringBuilder源码差不多,就是安不安全的问题)

StringBuffer sb1 = new StringBuffer();
//执行实际源码为:char[] value = new char[16]; 底层创建了一个长度是16的数组。
sb1.append('a'); //value[0] = 'a';
sb1.append('b'); //value[1] = 'b';

StringBuffer sb2 = new StringBuffer("abc");
//执行实际源码为:char[] value = new char["abc".length() + 16]; 字符串的长度外加16个长度数组。

//问题1:System.out.println(sb2.length()); 输出为3呢,还是3+16呢?
//答案:其实和加不加16没关系,因为length返回的count,是数组中有多少数据。

//问题2:既然开始定义长度为16,一旦满了就会有一个扩容问题?
//如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
//append方法中,调用了ensureCapacityInternal(确保容量完整的)方法,进而满足扩容。
//默认情况下扩容为原来容量的2倍(向左进位1,就是乘于2)+2,同时将原有数组中的元素复制到新的数组中。

开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity)。可以直接定义容量大小,区别就是安全不安全。

12. StringBuffer 和 StringBuilder类常用的方法

StringBuffer和StringBuilder的常用方法差不多,都是一样:



当append和insert时,如果原来value数组长度不够,可扩容。

package com.holmes.java04;

import org.junit.Test;

public class StringBufferBuilderTest {
    @Test
    public void test1(){

        StringBuffer sb1 = new StringBuffer("abc");
        sb1.append(1);
        sb1.append('1');
        System.out.println(sb1);

        //注意:像delete这样的方法,删除的索引是左闭右开!!
        sb1.delete(2,4);
        System.out.println(sb1);

        sb1.replace(2,4,"hello");
        System.out.println(sb1);

        sb1.insert(2,"c");
        System.out.println(sb1);

        sb1.reverse();
        System.out.println(sb1);

        System.out.println( sb1.indexOf("c"));

        System.out.println(sb1.substring(2,4));

        System.out.println(sb1.charAt(5));

        sb1.setCharAt(0,'m');
        System.out.println(sb1);
    }
}

方法链是一个很重要的形式,方法链的原理:


13. 对比String ,StringBuffer,StringBuilder的效率

package com.holmes.java04;

import org.junit.Test;

public class StringBufferBuilderTest {
    @Test
    public void test1(){
        long startTime = 0;
        long endTime = 0;
        String text = <

以上是关于Java 基础知识点 笔记总结 的主要内容,如果未能解决你的问题,请参考以下文章

关于JAVA 反射 基础知识/编码经验的一些总结

关于JAVA 异常 基础知识/编码经验的一些总结

关于JAVA 反射 基础知识/编码经验的一些总结

关于JAVA 异常 基础知识/编码经验的一些总结

Java 基础知识点 笔记总结

Java 基础知识点 笔记总结