美文网首页spring boot
Java知识点(一)基础

Java知识点(一)基础

作者: AC编程 | 来源:发表于2019-04-08 21:31 被阅读134次

    一、Java中的作用域有哪些?

    成员变量的4种作用域对比

    作用域与可见性 当前类 同一package 子类 其他package
    public
    private × × ×
    protected ×
    default × ×

    二、ArrayList和Vector的区别

    1. Vector的方法都是同步的,是线程安全的。而ArrayList的方法是线程不安全的,不是同步的。由于线程的同步必然要影响性能,因此,ArrayList一般多用于单线程环境下,而Vector一般多用于多线程环境下。
      以add方法为例,源码如下
      ArrayList add 方法源码
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    

    Vector add方法源码

        public synchronized boolean add(E e) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }
    
    1. 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小。ArrayList、Vector默认容量都是10。如果预先能了解到有多少数据,应该给ArrayList或Vector一个初始值,以减少集合自动扩容的次数,提高程序性能。
    //指定集合初始值
    ArrayList<String> list = new ArrayList<>(100);
    

    三、HashMap、Hashtable、ConcurrentHashMap的原理与区别

    HashTable

    • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
    • 初始size为11,扩容:newsize = olesize*2+1
    • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

    HashMap

    • 底层数组+链表实现,可以存储null键和null值,线程不安全
    • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
    • 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
    • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
    • 计算index方法:index = hash & (tab.length – 1)

    ConcurrentHashMap

    • 底层采用分段的数组+链表实现,线程安全
    • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍
    • Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术

    Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好

    四、Java集合框架图

    黄色的代表接口,绿色的是抽象类,蓝色的具体类


    Collection Map

    五、Java的八种基本数据类型

    Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
    1. 四种整数类型(byte、short、int、long)

    • byte:8 位,用于表示最小数据单位,如文件中数据,-128~127
    • short:16 位,很少用,-32768 ~ 32767
    • int:32 位、最常用,-231-1~231 (21 亿)
    • long:64 位、次常用

    注意事项: int i=5; // 5 叫直接量(或字面量),即 直接写出的常数。 整数字面量默认都为 int 类型,所以在定义的 long 型数据后面加 L或 l。 小于 32 位数的变量,都按 int 结果计算。

    2. 两种浮点数类型(float、double)

    • float:32 位,后缀 F 或 f,1 位符号位,8 位指数,23 位有效尾数
    • double:64 位,最常用,后缀 D 或 d,1 位符号位,11 位指数,52 位有效尾数

    注意事项:浮点数字面量默认都为 double 类型,所以在定义的 float 型数据后面加F 或 f;double 类型可不写后缀,但在小数计算中一定要写 D 或 X.X 。浮点数是不精确的,不能对浮点数进行精确比较。

    3. 一种字符类型(char)

    • char:16 位,是整数类型,用单引号括起来的 1 个字符(可以是一个中文字符),使用 Unicode 码代表字符,0~2^16-1(65535)

    注意事项:不能为 0个字符。
    转义字符:\n 换行 \r 回车
    两字符 char 中间用“+”连接,内部先把字符转成 int 类型,再进行加法运算,char 本质就是个数!二进制的,显示的时候,经过“处理”显示为字符。

    4. 一种布尔类型(boolean)
    true 真 和 false 假

    类型转换:char--> 自动转换:byte-->short-->int-->long-->float-->double
    强制转换:①会损失精度,产生误差,小数点以后的数字全部舍弃。②容易超过取值范围

    记忆:8位:Byte(字节型) 16位:short(短整型)、char(字符型) 32位:int(整型)、float(单精度型/浮点型) 64位:long(长整型)、double(双精度型) 最后一个:boolean(布尔类型

    代码测试

     long n = 4;  // 编译通过,可以不加L或l,4为int可以用long来装
     long n2 = 4L; // n == n2  为true
            
     float f = 2.4; // 编译不通过,2.4要加f 因为浮点数字面量默认都为double类型
     float f2 = 2.4f; // 编译通过
         
     double d = 2.4; // d == f2  为false  因为一个为float一个为double
     double d2 = 2.4f; // 编译通过,2.4f为float 可以用double来装
            
     // f2 == d2 为true
    
       char a ='a';
       char b = 'b';
            
       char c = '陈'; // 编译通过
       char d = 'a'+'b'; // A
            
       char e = a+b; // 编译不通过
       char f = 'ab'; // 编译不通过
       char g = ''; // 编译不通过
    

    六、Java中异常处理机制

    1. Java异常
    异常是什么?不正常的事件。运行程序时,它没有按照我们的预期执行,也就是不正常的运作,就叫异常。Java标准库内建了一些通用的异常。Throwable派生Error、Exception。Error类及其子类代表的是JVM本身的错误,并非程序的问题,不能由程序员通过代码进行处理。Exception及其子类代表的是在程序运行过程中产生的不期望发生的异常事件,可以被Java异常处理机制进行处理,是异常处理的核心。

    JDK8的异常类导图

    2. 异常处理语句及语法
    在写代码处理异常的时候,有两种不同的处理方式:

    • 1.使用try..catch..finally处理
    • 2.使用throws声明抛给函数调用者去处理

    try...catch..finally语句块

    try{
         //1.存放可能发生异常的代码。
         //2.如果没有发生异常,执行finally块代码(如果有finally块)并往下执行代码,否则,直接尝试去匹配catch块。
     
    }catch(IOException e1){
        //每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。
        //catch后面的括号定义了异常类型和异常参数(局部的)。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
        //在catch块中可以使用这个块的异常参数来获取异常的相关信息(getMessage()、printStackTrace()、getCause())。
    }catch(Exception e2){
        //...
    }finally{
        //finally块可要可不要。
       //一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
      //finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
    }
    

    需要注意的是:
    1.try块发生异常,那么try块中发生异常的那一行以下的代码都不会执行。
    2.无论异常发生与否,异常是否有catch匹配处理,finally块都会执行。
    3.如果catch中有return,先将return值暂存起来,再执行finally,最后再return

    public class Client {
        public static void main(String[] args) {
            System.out.println("result:"+fun());
        }
        
        static int fun() {
            int a = 1;
            try {
                a =2;
                int b = 1/0;
                System.out.println(" a = "+a); //异常后面的代码不会执行
                return a; //异常后面的代码不会执行
            }catch(Exception e){
                a =3;
                System.out.println("catch a = "+a);
                return a; //将a的值暂存起来,return 的时候返回出去, 再执行finally,最后执行return
            }finally {
                a = 4;
                System.out.println("finally a = "+a);
            }
        }
    }
    

    运行结果:

    catch a = 3
    finally a = 4
    result:3
    
    public class Client {
        public static void main(String[] args) {
            System.out.println("result:"+fun2());
        }
        
        static int fun2() {
            int a = 1;
            try {
                a =2;
                System.out.println(" a = "+a); 
                return a;//将a的值暂存起来,return 的时候返回出去, 再执行finally,最后执行return
            }catch(Exception e){
                 //因为没有异常,所以下面代码都不会执行
                a =3;
                System.out.println("catch a = "+a);
                return a; 
            }finally {
                a = 4;
                System.out.println("finally a = "+a);
            }
        }
    }
    

    运行结果:

     a = 2
    finally a = 4
    result:2
    

    3. 非检查异常 & 检查异常
    非检查异常(unchecked exception):
    包括Error和RuntimeException以及他们的子类。java编译器在编译时,不会提示和发现这样的异常,不要求在程序预处理这些异常,但是如果有异常产生,则异常将由JVM进行处理。当然如果你觉得可能会有异常就可以使用try..catch处理或throws抛出。

    检查异常(checked exception):
    包括除了Error和RuntimeException的其他异常。java编译器强制要求程序员预处理,不然编译器就会报错不让你通过。

    非检查异常 & 检查异常

    4. Java自定义异常,应该继承Exception还是Runtime Exception
    RuntimeException属于Exception的子类
    Exception是在编译时候如果有异常就可以检查出来,比较严谨!
    RuntimeException比较特殊,他及其子类对象,属于运行时候检查的异常,如果语法没有错误他不会在编译时候报异常的,只有运行的时候才会抛出异常!

    至于继承谁,得看类具体的功能,其实这两点已经说的很明确了,如果继承了Exception要么抛出去给上级调用者,要么调用异常代码的时候进行捕捉,有相对应的处理方式。如果继承的是RuntimeException,可以不用抛,也可以不用捕捉但是问题是在运行的过程中才会展现出来,一但出错,后面程序将无法继续运行。

    七、String,StringBuilder,StringBuffer三者的区别

    • String : 字符串常量,不可变,非线程安全;适合声明一个字符串常量,不适合做连接字符串操作。
    • StringBuilder:字符串变量,可变,非线程安全;适用于单线程下在字符缓冲区进行大量操作的情况。
    • StringBuffer:字符串变量,可变,线程安全;适用多线程下在字符缓冲区进行大量操作的情况。

    八、Class.forName的作用以及为什么要用它

    我们查看Class.forName源码,可看到注释为:
    Returns the {@code Class} object associated with the class or
    * interface with the given string name
    大概意思是:使用Class类中静态forName()方法获得与字符串对应的Class对象

    Class.forName作用为:给定一个字符串变量,它代表一个类的包名和类名,通过Class.forName实例化它

    //方式一
    A a = (A)Class.forName("pacage.A").newInstance(); 
    //方式二
    A a = new A(); 
    

    方式一和方式二效果是一样的。

    在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:
    1、这个类已经加载;
    2、这个类已经连接了。

    而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

    现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class.forName方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用Class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。Java中工厂模式经常使用newInstance()方法来创建对象

    new关键字和newInstance()方法的区别:
    newInstance: 弱类型,低效率,只能调用无参构造。
    new:强类型,相对高效,能调用任何public构造。

    九、什么是序列化?序列化的作用,应用场景

    序列化:所谓java对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象,Java通过实现Serializable接口来实现序列化。
    序列化作用:序列化将对象流化,然后进行传输和存储。再通过反序列化将流重组成对象。
    应用场景:对象序列化后存在在文件中;对象经过分布式系统进行网络传输;

    十、Collection和Collections有什么区别

    • Collection是集合类的上级接口,继承与他的接口主要有Set 和List
    • Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作

    十一、int和Integer有什么区别

    int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。

    int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况。要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。

    在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。

    另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。

    十二、是否可以从一个static方法内部发出对非static方法的调用

    不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部发出对非static方法的调用。

    十三、创建对象的几种方式

    new、反射、反序列化、Clone

    相关文章

      网友评论

        本文标题:Java知识点(一)基础

        本文链接:https://www.haomeiwen.com/subject/pwdciqtx.html