java的一些基础知识的总结

本文深入探讨Java中的关键概念,包括StringBuilder与StringBuffer的选择、Integer缓存机制、对象实例化流程、ArrayList与LinkedList的区别、逻辑运算符的差异及内存管理等,帮助读者掌握Java编程的精髓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.StringBuilder替代String拼接,面试中经常会遇到

String、StringBuilder、StringBuffer的区别

最佳答案:String 类作为java语言中最常见的字符串类被广泛使用,如果在做大量字符串拼接效率会变得比较低,因为虚拟机需要不断地将对象引用指向新的地址。

因此,一般方法内的私有变量推荐使用StringBuilder来完成,如果是多线程需要同步的自然选用StringBuffer。

String是字符串常量,由final修饰,不可被继承,String是常量,创建之后不能更改。

StringBuilder字符串变量(非线程安全)与StringBuffer一样都继承和实现了同样的接口和类,方法除了没使用synch修饰以外基本一致,不同之处在于最后toString的时候,会直接返回一个新对象

public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}

2.对参数未做空验证,就开始判断值是否相等

将常量放在equals方法的左边,能防止NullPointerException

总结:主要是要保证String 类equals的实现方法,要保证equals方法的左边常量不能为null

3.仔细观察下面两张图 注意控制台输出结果

 

Integer类型当正整数小于128时是在内存栈中创建值的,并将对象只想这个值,当比较两个栈引用时因为是同一地址引用两者则相等。当大于127时将会调用new Integer(),两个整数对象地址引用不相等了。

4.下面再来一题,思考输出结果

解析过程 :

  • ==操作符专门用来比较两个变量的值,比较变量所对应的内存中存储的数值
  • equals比较两个独立对象的内容,好比是去比较两个人的长相,比较的两个对象是独立的。

   自动装箱和自动拆箱

  • JVM会自动维护八种基本数据类型的常量池,第一次执行Integer i1=100时,会将i1放入常量池,i2的数据也是100,所以不再去创建新的对象,所以,i1==i2 输出结果为true。
  • Integer并不是基本数据类型,就涉及到了自动装箱和自动拆箱的概念。自动装箱就是Java自动将原始类型值转换为对应的对象,比如将int的变量类型转换成Integer对象,该过程称之为装箱,反之将Integer对象转换为int类型的值,这个过程叫做拆箱。
  • 装箱和拆箱时自动进行而并非认为转换,所以就称之为自动装箱和自动拆箱。原始类型byte(8),short(16),char(16),int(32),long(64),float(32),double(64)和boolean(1)对应的封装类为:Byte,Short,Character,Integer,Long,Float,Double,Boolean。
public class Test {
    public static void main(String[] args) {
        // byte
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
        System.out.println("包装类:java.lang.Byte");
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
        System.out.println();

        // short
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
        System.out.println("包装类:java.lang.Short");
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
        System.out.println();

        // int
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
        System.out.println("包装类:java.lang.Integer");
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
        System.out.println();

        // long
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
        System.out.println("包装类:java.lang.Long");
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
        System.out.println();

        // float
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
        System.out.println("包装类:java.lang.Float");
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
        System.out.println();

        // double
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
        System.out.println("包装类:java.lang.Double");
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
        System.out.println();

        // char
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
        System.out.println("包装类:java.lang.Character");
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
        System.out.println("最小值:Character.MIN_VALUE="
                + (int) Character.MIN_VALUE);
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
        System.out.println("最大值:Character.MAX_VALUE="
                + (int) Character.MAX_VALUE);
    }
}

输出结果 

 int类型的常量池中初始化范围为:-128-127.所以当Integer i1,i2=100时,都是在自动装箱的过程中从常量池中取数值,而Integer i3,i4=150时,不在常量池的范围内,所以自动装箱的过程中需要new一个对象,所以 i3==i4输出结果为false

5.下面哪个流类时属于面向字符的输入流()

A  BufferedWriter           

B  FileInputStream          

C  ObjectInputStream          

D  InputStreamReader

答案:D

解析:以InputStream(输入)/OutputStream(输出)为后缀的都是字节流。以Reader(输入)/Writer(输出)为后缀的时字符流

6.String能被继承吗?为什么?

  不可以,因为String类有final修饰符,final修饰的类是不能被继承的,实现的细节不允许改变。

String str="a"  和String str=new String("a")是有差别的。String str="a"默认调用的是String.valueOf来返回实例对象

比如String n=1; 调用的是

public static String valueOf(int i){return Integer.toString(i);}

String str=new String("a")调用的是

public String(String original) {
this.value = original.value;
this.hash = original.hash;
}

最后我们的变量都是存放在一个char类型的数组里private final char value[];

7.ArrayList和LinkedList的区别

ArrayList和LinkedList都实现了List接口

  • ArrayList是基于索引的数据接口,底层是数组。可以以O(1)的事件复杂度对元素进行随机访问。LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素连接在一起,查找某个元素的时间复杂度是O(n)。
  • LinkedList的插入,添加和删除速度更快,因为元素被添加到集合的认一位置的时候,不需要像数组那样重新计算大小或更新索引。
  • LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向后一个元素。

8.讲讲类的实例化过程,比如父类静态方法,成员变量,构造方法和子类静态方法,构造方法,成员变量。当new的时候,他们的执行顺序

package com.springsecurity.springsecurity.security.model;

/**
 * @Author zhaomengxia
 * @create 2019/8/8 17:14
 */
public class ClassLoaderTest {

    public static void main(String[] args) {
        son son = new son();
    }
}

class parent {
    private static int a = 1;
    private static int b;
    private int c = initc();

    {
        System.out.println("父类代码块");
    }

    public parent(int c) {
        this.c = c;
        System.out.println("父类有参构造方法");
    }

    public parent() {
        System.out.println("父类无参构造方法");
    }

    int initc() {
        System.out.println("3.父类成员变量赋值:---> c的值" + c);
        this.c = 12;
        System.out.println("3.父类成员变量赋值:---> c的值" + c);
        return c;
    }

    static {
        b = 1;
        System.out.println("1.父类静态代码块:赋值b成功");
        System.out.println("1.父类静态代码块:a的值" + a);
    }


}

class son extends parent {
    private static int sa = 1;
    private static int sb;
    private int sc = initc2();

    public son() {
        System.out.println("子类空参构造方法");
    }

    static {
        sb = 1;
        System.out.println("2.子类静态代码块:赋值sb成功");
        System.out.println("2.子类静态代码块:sa的值" + sa);
    }

    {
        System.out.println("子类代码块");
    }

    int initc2() {
        System.out.println("5.子类成员变量赋值--->:sc的值" + sc);
        this.sc = 12;
        return sc;
    }

    public son(int c, int sc) {
        super(c);
        this.sc = sc;
        System.out.println("子类构造方法");
    }

    public son(int sc) {
        this.sc = sc;
        System.out.println("子类构造方法");
    }
}

执行结果:

故意改变父类和子类静态代码块,构造方法,代码块和成员变量赋值的顺序结果如下,发现执行顺序一致 

package com.springsecurity.springsecurity.security.model;

/**
 * @Author zhaomengxia
 * @create 2019/8/8 17:14
 */
public class ClassLoaderTest {

    public static void main(String[] args) {
        son son = new son();
    }
}

class parent {
    private static int a = 1;
    private static int b;
    private int c = initc();

    public parent(int c) {
        this.c = c;
        System.out.println("父类有参构造方法");
    }

    public parent() {
        System.out.println("父类无参构造方法");
    }

    {
        System.out.println("父类代码块");
    }

    static {
        b = 1;
        System.out.println("1.父类静态代码块:赋值b成功");
        System.out.println("1.父类静态代码块:a的值" + a);
    }

    int initc() {
        System.out.println("3.父类成员变量赋值:---> c的值" + c);
        this.c = 12;
        System.out.println("3.父类成员变量赋值:---> c的值" + c);
        return c;
    }

}

class son extends parent {
    private static int sa = 1;
    private static int sb;
    private int sc = initc2();

    public son() {
        System.out.println("子类空参构造方法");
    }

    {
        System.out.println("子类代码块");
    }

    int initc2() {
        System.out.println("5.子类成员变量赋值--->:sc的值" + sc);
        this.sc = 12;
        return sc;
    }

    static {
        sb = 1;
        System.out.println("2.子类静态代码块:赋值sb成功");
        System.out.println("2.子类静态代码块:sa的值" + sa);
    }

    public son(int c, int sc) {
        super(c);
        this.sc = sc;
        System.out.println("子类构造方法");
    }

    public son(int sc) {
        this.sc = sc;
        System.out.println("子类构造方法");
    }
}

执行结果如下 

  •  静态方法块或则静态变量优先加载。即父类子类的所有静态方法优先执行,但是要遵循先父类后子类。静态方法中的静态变量是按顺序的,即谁在前先加载谁。接着就是父类剩余的成员变量和构造方法,先给父类的成员变量赋值,再执行父类的虚函数,最后执行相应的父类构造方法;然后才是子类,子类也是先给成员变量赋值,接着是子类的代码块,最后执行子类相应的构造方法。

9.&与&&的区别

  • &逻辑与,&&短路与。&与&&都要求左右两端的布尔值都为true时表达式的值才为true。&&如果左边的表达式的值为false,右边的表达式会被直接短路掉,不会进行运算。当我们判断用户名不是null而且不是空字符串时,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意逻辑或(|)和短路或(||)也是同样的差别。

思考一下这个输出结果,结果是不是 2  2

这个结果是不是3  4

10.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法

  •    通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则存放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,故堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;
  • 方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;
  • 程序中的字面量如直接书写的100,"hello"和常量都是放在常量池中,常量池是方法区的一部分。
  • 栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足会引发OutOfMemoryError。
String str = new String("hello");

上面语句中,str是放在栈上;用new创建出来的字符串对象放在堆上 ;而"hello"这个字面量是放在方法区的。

11.Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?

  •  Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。

12.switch是否能够作用在byte上,是否能作用在long上,是否能作用在String上?

  • 在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。
  • 从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型
  • 从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

13.用最有效率的方法计算2乘以8?

  • 2<<3(左移三位相当于乘以2的3次方,右移三位相当于除以2的3次方)

14.数组有没有length()方法?String有没有length()方法

数组有length,String有length()方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值