JavaSE——基础

1.访问修饰符public,private,protected以及不写(默认)的区别?

修饰符当前类同包子类其他包
public
protected×
default××
private×××

 

2.JAVA中的几种基本类型,各占用多少字节?

下图单位是bit,非字节 1B=8bit 

 

数据类型大小范围默认值
byte(字节)8-128~1270
short(短整型)16-32768~327680
int(整型)32-2147483648~21474836480
long(长整型)64-9233372036854477808~92333720368544778080
float(浮点型)32-3.40292347E+38~3.40292347E+380.0f
double(双精度)64-1.79769313486231570E+308~1.79769313486231570E+3080.0d
char(字符型)16'\u000~u\ffff''\u0000'
boolean(布尔型)1true/falsefalse

3.float f = 3.4;这个表达式是否正确?

不正确。在Java里面,没有小数点的默认是int类型,有小数点的默认是double类型。因此3.4是双精度数,将双精度浮(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化),会造成精度损失,因此需要强制类型转换 float f = (float)3.4 或者写成 float f = 3.4f 。

4.short s1 = 1; s1 = s1 + 1; 这个表达式有错吗?short s1 = 1; s1 += 1; 这个表达式有错吗?

前面这个表达式,由于1是int类型,因此 s1 + 1 的运算结果也是int类型,再把int类型的结果赋值给short类型的s1是会报错的,需要对结果进行强制类型转换才能通过编译。后面这个表达式, s1 += 1; 相当于 s1 = (short)(s1 + 1); 表达式,其中会有隐含的强制类型转换,可以正常通过编译。

5.int和Integer有什么区别?

Java是一个近乎纯洁的面向对象编程的语言,但是为了编程的方便还是引入了基本数据类型。为了能够将这些基本数据类型当作对象操作,Java为每一个基本数据类型都引入了相应的包装类型(Wrapper Class),int类型的包装类型就是Integer。从Java5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

Java为每个原始类型(基本数据类型)提供了相应的包装类型:

原始类型:byte,short,int,long,float,double,char,boolean

包装类型:Byte,Short,Integer,Long,Float,Double,Character,Boolean

        Integer a = new Integer(3);
        Integer b = 3;//将3自动装箱成Integer类型
        int c = 3;
        System.out.println(a == b);//false 两个引用没有引用同一对象
        System.out.println(a == c);//true a自动拆箱成int类型再和c比较

下面这段代码也和自动装箱/拆箱有关系:

        Integer f1 = 100, f2 = 100, f3 = 128, f4 = 128;
        System.out.println(f1 == f2); // true
        System.out.println(f3 == f4); // false

为什么第一个运算的结果是true,第二个却是false呢?

首先注意,f1、f2、f3和f4都是Integer对象引用,所以下面的==运算比较的不是值而是引用。

装箱的本质是什么呢?是当我们给一个Integer对象赋一个int类型值的时候,会调用Integer类的静态方法valueOf(),看看valueOf()方法的源代码就知道为什么会产生这样的结果。

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

IntegerCache是Interger类的内部类,其代码如下:

 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

简单得说,如果整型字面量的值的范围在-128到127之间,那么就不会new一个新的Integer对象,而是直接引用常量池中的Integer对象。

6.&和&&的区别?

&运算符有两种用法,一是按位与,二是逻辑与。&&运算符是短路与。

逻辑与跟短语与的差别是非常大的,虽然二者都要求运算符左右两边的布尔值都是true整个表达式的值才是true,但是如果&&运算符左边的表达式是false的话,右边的表达式会直接被短路掉,不会进行运算,这就是&&运算符被称为短路运算的原因。

很多时候我们需要用到的可能都是&&而不是&,例如在验证用户登录的时候,判定用户名不是null而且不是空字符串,应当写为:userName != null && !userName.equals(""),二者的顺序不能交换,更不能使用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。

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

Math.round(11.5)的结果是12,Math.round(-11.5)的结果是-11。

四舍五入的原理是在参数上加0.5,然后进行向下取整。

那么Math.round(11.6)和Math.round(-11.6)的结果又是什么呢?

答案是12和-12。

11.6 + 0.5 = 12.1,然后向下取整为12;

-11.6 + 0.5 = -11.1,然后向下取整为-12。

要特别理解向下取整是什么意思。

8.两个对象值相同(x.equals(y) == true)但是却可有不同的hashCode,这句话对不对?

不对。如果两个对象x和y满足equals()结果为true,那么它们的哈希码(hashCode)应当相同。

Java对于equals)(方法和hashCode()方法是这样规定的:

1.如果两个对象相同(equals()方法返回true),那么它们的hashCode值一定要相同;

2.如果两个对象的hashCode相同,它们并不一定相同。

当然,你未必要按照要求去做,但是如果你违背了上述原则,就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁地冲突将会造成存取性能急剧下降)。

9.switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

switch可作用于char byte short int
switch可作用于char byte short int对应的包装类
switch不可作用于long double float boolean,包括他们的包装类

switch中可以是字符串类型,String(jdk1.7之后才可以作用在string上)

10.String s = "Hello";s = s + "world!";这两行代码执行后,原始的String对象中的内容到底变了没有?

没有。因为String是不可变类(immutable class),不可变类,顾名思义就是说类的实例是不可被修改的。实例的信息是在创建的时候提供,并且在整个生命周期中都不可改变。在这段代码中,s原来指向一个String对象,内容是“hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为”helloworld!",原来那个对象还存在内存中,只是s这个引用变量不再指向他了。

       通过上面的说明,我们很容易得出一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为,String对象建立后不能改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,他允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。

       对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。

       至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。

11.final在java中有什么作用?

  1. final修饰类中的属性或者变量:

            无论属性是基本类型还是引用类型,final所起的作用都是变量里面存放的“值”不能变。

            这个值,对于基本类型来说,变量里面放的就是实实在在的值,如1,“abc”等。

            而引用类型变量里面放的是个地址,所以用final修饰引用类型变量指的是它里面的地址不能变,并不是说这个地址所指向的对象或数组的内容不可以变,这个一定要注意。

             例如:类中有一个属性是final Person p=new Person("name"); 那么你不能对p进行重新赋值,但是可以改变p里面属性的值,p.setName('newName');

            final修饰属性,声明变量时可以不赋值,而且一旦赋值就不能被修改了。对final属性可以在三个地方赋值:声明时、初始化块中、构造方法中。总之一定要赋值。    

2. final修饰类中的方法:

           作用:可以被继承,但继承后不能被重写。

3. final修饰类:

       作用:类不可以被继承。

      思考一个有趣的现象:

       byte b1=1;

       byte b2=3;

       byte b3=b1+b2;//当程序执行到这一行的时候会出错,因为b1、b2可以自动转换成int类型的变量,运算时java虚拟机对它进行了转换,结果导致把一个int赋值给byte-----出错

       如果对b1 b2加上final就不会出错

       final byte b1=1;

       final byte b2=3;

       byte b3=b1+b2;//不会出错,相信你看了上面的解释就知道原因了。

12.如何将字符串反转?

1.通过String类的charAt()的方法来获取字符串中的每一个字符,然后将其拼接为一个新的字符串。

    public static String charAtReverse(String str){
        String reverse = "";
        for (int i = 0; i < str.length(); i++) {
            reverse = str.charAt(i) + reverse;
        }
        return reverse;
    }

2.通过String的toCharArray()方法可以获得字符串中的每一个字符串并转换为字符数组,然后用一个空的字符串从后向前一个个的拼接成新的字符串。

    public static String reverseCharArray(String str){
        char[] array = str.toCharArray();
        String reverse = "";
        for (int i = array.length - 1; i >= 0; i--) {
            reverse += array[i];
        }
        return reverse;
    }

3.通过StringBuiler的reverse()的方法,最快的方式。

    public static String reverseStringBuilder(String str){
        StringBuilder stringBuilder = new StringBuilder(str);
        return stringBuilder.reverse().toString();
    }

4.通过递归的方式。首先通过String的toCharArray()方法获取字符数组,再使用递归方式交换字符数组中第1个和第n个,第2个和第n-1个,以此类推,最后得到逆序的字符数组,再通过String的valueOf()方法将字符数组变成字符串。

    public static String reverseString(String str){
        char[] s = str.toCharArray();
        helper(0,s.length - 1,s);
        return String.valueOf(s);
    }

    private static void helper(int start, int end, char[] s) {
        if (start >= end){
            return;
        }
        char temp = s[start];
        s[start] = s[end];
        s[end] = temp;

        helper(start+1,end-1,s);
    }

13.抽象类必须要有抽象方法吗?

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。
如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

14.java中的IO流分为几种?

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

15.BIO、NIO、AIO有什么区别?

一、同步阻塞I/O(BIO)

同步阻塞I/O,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制来改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务端资源要求比较高,并发局限于应用中,在jdk1.4以前是唯一的io现在,但程序直观简单易理解

二、同步非阻塞I/O(NIO)

同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,jdk1,4开始支持

三、异步非阻塞I/O(AIO)

异步非阻塞I/O,服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程进行处理。AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,jdk1.7开始支持。

四、IO与NIO区别

  1. IO面向流,NIO面向缓冲区
  2. IO的各种流是阻塞的,NIO是非阻塞模式
  3. Java NIO的选择允许一个单独的线程来监视多个输入通道,可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入或选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道

五、同步与异步的区别

同步:发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生

异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发

同步异步关注点在于消息通信机制

阻塞与非阻塞关注的是程序在等待调用结果时(消息、返回值)的状态

  • 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程

不同层次

         1. CPU层次:操作系统进行IO或任务调度层次,现代操作系统通常使用异步非阻塞方式进行IO(有少部分IO可能会使用同步非阻塞),即发出IO请求后,并不等待IO操作完成,而是继续执行接下来的指令(非阻塞),IO操作和CPU指令互不干扰(异步),最后通过中断的方式通知IO操作的完成结果。

        2. 线程层次:操作系统调度单元的层次,操作系统为了减轻程序员的思考负担,将底层的异步非阻塞的IO方式进行封装,把相关系统调用(如read和write)以同步的方式展现出来,然而同步阻塞IO会使线程挂起,同步非阻塞IO会消耗CPU资源在轮询上,3个解决方法:

  1. 多线程(同步阻塞)
  2. IO多路复用(select、poll、epoll)
  3. 直接暴露出异步的IO接口,kernel-aio和IOCP(异步非阻塞)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值