开刷BAT等大厂最全面试题集锦

(一) java基础面试知识点

1.java中==和equals和hashCode的区别

答:首先回答的是三个定义

      1> ==是一个运算符号,是用来比较两个变量是否相等

      2> equals是Objec类的方法,用于比较两个对象是否相等,默认Object类的equals方法是比较两个对象的地址,跟==的结果一样。Object的equals方法如下:

    public boolean equals(Object obj) {  

        return (this == obj);  

    }  

     3> hashCode也是Object类的一个方法。返回一个离散的int型整数。在集合类操作中使用,为了提高查询速度。(HashMap,HashSet等)

接下来回答的是三者的区别

    1>如果比较的是基本数据类型,那就直接用==这个运算符号来比较他们的值

    2>如果比较的是符合数据类型,就是类,

        这个时候如果用:(==)进行比较的时候,比较的是在内存中的存放地址,(除非是同一个new出来的对象,他们的地址是一样的),如果用equals进行比较,要分为两种情况,第一种如果object类中的equals方法并没有被其他(String,integer,date)这些类覆盖,那么比较的是对象的内存地址,如果被覆盖了比较的是在比较两个对象的value值是否相等,在接下来是hashcode的理解,如果两个对象根据equals()方法比较是相等,那么调用这两个对象中任意一个对象的hasnCode方法都必须产生同样的整数结果.如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashcode方法,则不一定要产生相同的结果

接下来还可以说明注意点

当在代码中需要覆盖equals时总要覆盖hashCode,如果不进行覆盖的话就会违反objecthashCode的通用约定,从而导致该类无法结合所有从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生相同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

2.int、char、long各占多少字节数

答:1字节: byte , boolean

     2字节: short , char

     4字节: int , float

     8字节: long , double 

注:1字节(byte)=8位(bits)


3.int与integer的区别

答:1、Integer是int的包装类,int则是java的一种基本数据类型 

    2、Integer变量必须实例化后才能使用,而int变量不需要 

    3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 

   4、Integer的默认值是null,int的默认值是0

关于Integer和int的比较

1、由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。

Integer i =newInteger(100);

Integer j =newInteger(100);

System.out.print(i == j); //false

2、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)

Integer i = newInteger(100);

int j =100;

System.out.print(i == j); //true

3、非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)

Integer i =newInteger(100);

Integer j =100;

System.out.print(i == j); //false

4、对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false

Integer i =100;

Integer j =100;

System.out.print(i == j); //true

Integer i =128;

Integer j =128;

System.out.print(i == j); //false

对于第4条的原因:

java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);,而java API中对Integer类型的valueOf的定义如下:

publicstaticIntegervalueOf(int i){

assert IntegerCache.high >=127;

if (i >= IntegerCache.low && i <= IntegerCache.high){

return IntegerCache.cache[i + (-IntegerCache.low)];

}

returnnew Integer(i);

}

java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了

如果有错误的地方,还请指正。


4.谈谈对java多态的理解

答:先从定义谈起,

1.面向对象的三大特性分别是,封装.继承.多态

2.什么是多态,指允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式

3.实现多态的技术称为:动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法

4.多态的作用:消除类型之间的耦合关系

多态的三个必要条件:

一:要有继承

二.要有重写

三.父类应用指向子类对象

多态的好处:

1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。

2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。

3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。

4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。

5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。


5.String、StringBuffer、StringBuilder区别

答:String 字符串常量,Stringbuffer字符串变量(线程安全),Stringbuilder字符串变量(非线程安全)

string和Stringbuffer和stringbuilder最大的区别在于,string是不可变的对象,当每次为String类型进行改变的时候其实都是生成了一个新的string对象,然后指针指向新的string对象,但凡生成新对象都会对性能产生影响,特别是当无用的对象在内存中占有特别多数量的时候,所以在经常改变字符串内容的时候尽量不要string,stringbuffer则是对自己对象本身进行操作,不会去生成新的对象,再改变对象应用

举个例子

 String S1 = “This is only a” + “ simple” + “ test”;

 StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个

 String S1 = “This is only a” + “ simple” + “test”; 其实就是:

 String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:

String S2 = “This is only a”;

String S3 = “ simple”;

String S4 = “ test”;

String S1 = S2 +S3 + S4;

这时候 JVM 会规规矩矩的按照原来的方式去做

由此可以对比出来大部分情况下Stringbuffer>String

接下来是要比较stringbuffer和stringbuilde

首先stringbuilde是一个可变的字符序列是在5.0的时候新增的,设计的主要作用是为了与stringbuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,他两者的方法基本相同的,总结下来stringbuffer多数用于多线程,stringbuilder多使用单线程

由此可以对比大部分情况下Stringbuilder>stringbuffer>string


6.什么是内部类?内部类的作用

答:内部类的定义,就是在一个类里面的类,预支对应,包含内部类的类称之为外部类

内部类可以分为:成员内部类,静态内部类,方法内部类,匿名内部类

内部类的作用有以下几点:

1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类

2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据

3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便

注意点:

(1)、内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。

(2)、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。

(3)、内部类声明成静态的,就不能随便的访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:  每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。


7.抽象类和接口区别

答:

1.抽象类可以提供成员方法.可以是各种成员变量,可以有静态代码块和方法,一个类只能继承一个抽象类

2.接口只能存在public abstract抽象方法,成员变量中必须使用public static final修饰,不可以含有静态代码块和静态方法,一个类可以实现多个接口


8.抽象类的意义,接口的意义

答:抽象类往往用来表示在对问题领域进行分析,设计中得出抽象概念,是对一系列看上去不同,但本质相同的具体概念的抽象,即对类的抽象,而接口是对行为的抽象.抽象类是对整个类进行抽象,包括属性,行为,但是接口却是对类局部(行为)进行抽象


9.抽象类与接口的应用场景

答:举个例子,飞机和飞鸟他俩的共性都是飞,那么飞机的类为airplane,飞鸟为bird,那么fly就可以作为一个抽象类,飞机和飞鸟继承抽象类

例如战斗机,直升飞机,战斗机的功能是战斗,那么战斗就是一个接口,直升飞机的功能是载人,那么载人就是一个接口,如果出来一个航空战斗机型号,既要战斗又要载人,那么就实现两个接口就好了


10.抽象类是否可以没有方法和属性?

答:抽象类可以不含抽象方法


开刷BAT等大厂最全面试题集锦_第1张图片

但是含抽象方法的类一定是抽象类

另注:

①java允许类、接口或者成员方法具有抽象属性,但不允许成员域或构造方法具有抽象属性

②如果一个类不具有抽象属性,则不能在该类的类体中定义抽象成员方法


11.泛型中extends和super的区别

答:请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。

生产者使用extends

如果你需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成< extends T>,比如List< extends Integer>,因此你不能往该列表中添加任何元素。

消费者使用super

如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成< super T>,比如List< super Integer>,因此你不能保证从中读取到的元素的类型。

即是生产者,也是消费者

如果一个列表即要生产,又要消费,你不能使用泛型通配符声明列表,比如List。


12.父类的静态方法能否被子类重写

答::父类的静态方法可以被子类继承,但是不能重写。

 先是父类代码:

public class Fu {  

public static void show() {  

System.out.println("父类的静态方法");  

    }  

public void method() {  

System.out.println("父类的一般方法");  

    }  

}  

     下面是子类代码:

public class Zi extends Fu {  

public static void main(String[] args) {  

Fu fu =new Zi();  

        fu.show();  

        fu.method();  

    }  

public static void show() {  

System.out.println("子类的静态");  

    }  

public void method() {  

System.out.println("子类的一般方法");  

    }  


}  

       输出结果是:

父类的静态方法

子类的一般方法


13.进程和线程的区别

:答

进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程:进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。


14.final,finally,finalize的区别

答:

final:修饰符(关键字)有三种用法:如果修饰类,不可被继承,如果修饰变量,不可被修改,如果修饰方法,不可被重写

finally:通常是房子try..........catch....的后面,构造总是执行代码块,意味着无论程序正常执行还是发生异常,这里面的代码只要jvm不关闭都能执行,可以将释放外部资源的代码卸载finally块中

finalize:object类中定义的方法,java中允许使用finalize()方法在垃圾收集齐将对象从内存中清楚出去之前做必要的清理工作,这个方法是由垃圾收集器在销毁对象前调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作.


15.序列化的方式,serializable和parcelable的区别

答:

1.在使用内存的时候parcelable类比serializable性能高,所以推荐使用parcelable类

2.serializable在序列化的时候回产生大量的临时变量,从而引起频繁的GC

3.parcelable不能使用在要要将数据存储在磁盘上的情况,尽管serializable效率地点,但是这种情况还是适合使用serializable

4.serializable的实现,只需要继承serializable即可,这只是给对象打了一个标记,系统会自动将其序列化

5.parcelable的实现,需要在类中添加一个静态成员变量CREATOR,这个变量需要继承Parcelable.Creator


16.静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

答:java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏.

原因:

1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。

2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。

3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。


17.静态内部类的设计意图

答:静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。

没有这个引用就意味着:

它的创建是不需要依赖于外围类的。

它不能使用任何外围类的非static成员变量和方法。


18.成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

答:内部类

内部类,即定义在一个类的内部的类。为什么有内部类呢?

我们知道,在java中类是单继承的,一个类只能继承另一个具体类或抽象类(可以实现多个接口)。这种设计的目的是因为在多继承中,当多个父类中有重复的属性或者方法时,子类的调用结果会含糊不清,因此用了单继承。

而使用内部类的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

静态内部类

说静态内部类之前,先了解下成员内部类(非静态的内部类)。

成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以它可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点:

成员内部类中不能存在任何static的变量和方法;

成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

静态内部类

静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。

没有这个引用就意味着:

它的创建是不需要依赖于外围类的。

它不能使用任何外围类的非static成员变量和方法。

其它两种内部类:局部内部类和匿名内部类

局部内部类

局部内部类是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

匿名内部类

1、匿名内部类是没有访问修饰符的。

2、new 匿名内部类,这个类首先是要存在的。

3、当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

4、匿名内部类没有明面上的构造方法,编译器会自动生成一个引用外部类的构造方法。


19.谈谈对kotlin的理解

答:2017年谷歌I/O大会的最后,谷歌宣布将Kotlin语言作为安卓开发的一级编程语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。

Kotlin的文件扩展名为.kt和.kts,使用Kotlin,你可以用更少的代码获得更多的功能。 而你写的代码越少,你犯的错误就越少。除此以外,他还有如下特点:

1> Kotlin编译为JVM字节码或JavaScript,方便在没有JVM的设备上运行。

2> Kotlin程序可以使用所有现有的Java框架和库,也就是说所有的现有程序不需要更改就可以直接被调用。

3> Kotlin可以轻松学习,平易近人。它的规则及其简单,语法规则少,易于学习。

4> Kotlin是开放源码,没有收费。虽然java也是开源语言,但是相比于其他的非开源的还是有一定优势的。

5> 将Java自动转换为Kotlin,有强迫症的也可以这么搞,不用逼死强迫症的。

6> Kotlin的空安全性很好

7> 空安全 Null Safety- 如上节所述,Kotlin避免了NullPointerException。

8> 扩展函数Extension Functions- Kotlin允许我们扩展现有类的功能,而不继承它们。意味着Kotlin提供了扩展具有新功能的类的能力,而无需继承类。


20.闭包和局部内部类的区别

答:

闭包就是把函数以及变量包起来,使得变量的生存周期延长。闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。

局部内部类是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

闭包有什么作用:简而言之,闭包的作用就是在Outer执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回Outer所占用的资源,因为Outer的内部函数Inner的执行需要依赖Outer中的变量。

闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含创建内部类的作用域的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。


21.string 转换成 integer的方式及原理

答:方式直接是用integer.valueOf(String str)这个方法(注:如果字符串的格式不对,这个方法会抛出NumberFormatException)

原理如下:

在Integer类中的定义如下:

public static Integer valueOf(String s) throws NumberFormatException{

        return new Integer(parseInt(s, 10));

}

这里因为parseInt方法返回的int型的,这里调用了一个构造函数产生了一个新的Integer实例.

这里关心的是parseInt方法,该方法代码如下:

public static int parseInt(String s, int radix)

throws NumberFormatException{

        if (s == null) {

                throw new NumberFormatException("null");

        }

        if (radix < Character.MIN_RADIX) {

                throw new NumberFormatException("radix " + radix +" less than Character.MIN_RADIX");

        }

        if (radix > Character.MAX_RADIX) {

                throw new NumberFormatException("radix " + radix +" greater than Character.MAX_RADIX");

        }

        int result = 0;

        boolean negative = false;

        int i = 0, max = s.length();

        int limit;

        int multmin;

        int digit;

        if (max > 0) {

        if (s.charAt(0) == '-') {

                negative = true;

                limit = Integer.MIN_VALUE;

                i++;

        } else {

                limit = -Integer.MAX_VALUE;

        }

        if (i < max) {

            digit = Character.digit(s.charAt(i++),radix);

            if (digit < 0) {

            throw NumberFormatException.forInputString(s);

            } else {

                result = -digit;

            }

    }

    while (i < max) {

    // Accumulating negatively avoids surprises near MAX_VALUE

    digit = Character.digit(s.charAt(i++),radix);

    if (digit < 0) {

        throw NumberFormatException.forInputString(s); 

    }

    if (result < multmin) {

        throw NumberFormatException.forInputString(s);  异常1

    }

    result *= radix;

    if (result < limit + digit) {

        throw NumberFormatException.forInputString(s);  异常2

    }

    result -= digit;

    }

    } else {

        throw NumberFormatException.forInputString(s);

    }

    if (negative) {

        if (i > 1) {

            return result;

        } else { /* Only got "-" */

            throw NumberFormatException.forInputString(s);

        }

    } else {

        return -result;

        }

}

很显然,该方法的第二个参数表示是基数(最常用的是十进制,还有十六机制,八进制等等).

如果字符串是空指针,直接抛出异常.

如果基础小于2或者大于36的话,抛出异常(这种情况一般不会出现,因为我们用的最多就是十进制的了).

如果是空字符串,也抛出异常,也就是max=0的情况了.

我们来关注下面的转换过程:

这里使用了Character中的静态方法digit,这个方法比较复杂,这里先说明它的功能:对于给定的基数,如果是合法的字符(可以转化为数字),返回该数字值,否则返回-1.比如digit('3',10)返回3,digit('a',10)返回-1.

这段程序看起来很简单,其实还真不容易看懂,这里先说明几个局部变量的含义吧:

result:记录返回值

negative:符号标志

i:字符串位置

s:字符串长度

limit:界限

multmin:也是一个界限

digit:当前字符表示的数字

先看第一个字符是否是'-'号,设定符号标志negative和极限值limit.

注意到limit一定是一个负值.

处理最高位,这里result保存的是负值,这样就可以对正负数统一处理.

关键就是这个while循环了,第一个if不用解释了,肯定是因为非法字符.

第二个if语句的含义:如果result小于multmin,会产生什么结果呢?

是不是一定会溢出呢?假设不会溢出,就是说结果必须>=limit.

result小于multmin,result至少应该位multmin-1,后面有result=result*radix=(multmin-1)*radix=multmin*radix-radix

该值肯定小于limit,其中multmin=limit/radix,注意这里都是负数.

所以假设不成里,如果result小于multmin的话,后面一定会溢出.

如果这里没有判断的话,溢出就麻烦了,正数也会变负数了.

第三个if语句的含义:在这条语句以前肯定没有溢出,但是有可能加上最后一位digit就溢出了,所以这个判断也是必要的.

后面的就比较好理解了,else是表示空字符串"".

如果是负数的还要看是否长度是1,就只是一个'-'号的情况.

如果是正数的话返回相反数就可以了.

这里有好多地方都有可能抛出异常,只要看明白了程序就知道这个异常是那条语句抛出的了,这里考虑溢出异常:异常1和异常2.

Ingeter.Max_VALUE=2147483647

下面的两条语句在不同的地方抛出异常.

Ingeter.valueOf("2147483648");这个在异常2抛出的.

Ingeter.valueOf("21474836471");这个在异常1抛出的.

这里简单的分析了String转化为Ingeter的过程,其实整个Ingeter类也就主要是这个方法了,Byte和Short都是调用这个方法的.

看看Byte的代码:

public static byte parseByte(String s, int radix)

throws NumberFormatException {

int i = Integer.parseInt(s, radix);

if (i < MIN_VALUE || i > MAX_VALUE)

throw new NumberFormatException(

"Value out of range. Value:/"" + s + "/" Radix:" + radix);

return (byte)i;

}

了解这个方法后就再也不会为Integer.valueOf()产生的异常感到意外了,特别是在JSP中,因为参数都是String型的,转换的时候动不动就出现异

常,你该知道怎么回事了吧.

内容摘自:https://blog.csdn.net/treeroot/article/details/92923

你可能感兴趣的:(开刷BAT等大厂最全面试题集锦)