2018.8.22日阿里巴巴java实习生面试答案整理

java基础:

1.基本数据类型

Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double。

2.基本数据类型所占字节长度

类型 占用字节 占用位数
byte 1 8
short 2 16
int 4 32
long 8 64
float 4 32
double 8 64
char 2 16
boolean 1 8

3.String,StringBuffer,Stringbuilder

1.首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
String最慢的原因:
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。

2. 再来说线程安全
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

3. 总结一下
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

4.StringBuffer和StringBuilder都继承自AbstractStringBuilder这个类,而AbstractStringBuilder和String都继承自Object这个类
5.String类没有append()、delete()、insert()这三个成员方法,而StringBuffer和StringBuilder都有这些方法
6.StringBuffer和StringBuilder中的append、delete、insert这几个成员方法都是通过System类的arraycopy方法来实现的,即将原数组复制到目标数组。

4.object有哪些方法

 1 registerNatives()   //私有方法
 2 getClass()    //返回此 Object 的运行类。
 3 hashCode()    //用于获取对象的哈希值。
 4 equals(Object obj)     //用于确认两个对象是否“相同”。
 5 clone()    //创建并返回此对象的一个副本。 
 6 toString()   //返回该对象的字符串表示。   
 7 notify()    //唤醒在此对象监视器上等待的单个线程。   
 8 notifyAll()     //唤醒在此对象监视器上等待的所有线程。   
 9 wait(long timeout)//在其他线程调用此对象的 notify()方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。  
 10 wait(long timeout, int nanos) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
 11 wait() //用于让当前线程失去操作权限,当前线程进入等待序列
 12 finalize() //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
关键字:native  java关键字,Native Method 用以修饰非 java 代码实现的方法(C || C++), 类似java调用非java代码的接口。

5.hashcode()与equle(),==的区别

1.0 equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。
1.1 equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。 
2.0 hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
2.1 1.基本数据类型,也称原始数据类型  byte,short,char,int,long,float,double,boolean   他们之间的比较,应用双等号(==),比较的是他们的值。
3.每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的   equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
****重写hashcode
class Person{
   int num;
   String name;
   public int hashCode(){
      return num*name.hashCode();
    } 
 }

 4.如果我们的对象要想放进hashSet,并且发挥hashSet的特性(即不包含一样的对象),则我们就要重写我们类的hashCode()和equal()方法了。像String,Integer等这种类内部都已经重写了这两个方法。

equals重新需要注意五点:
1   自反性:对任意引用值X,x.equals(x)的返回值一定为true. 
2   对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true; 
3   传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true 
4   一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变 
5   非空性:任何非空的引用值X,x.equals(null)的返回值一定为false 

高质量equals方法的诀窍: 
1).使用==符号检查“参数是否为这个对象的引用”。如果是,则返回true。这只不过是一种性能优化,如果比较操作有可能很昂贵,就值得这么做。 
2).使用instanceof操作符检查“参数是否为正确的类型”。如果不是,则返回false。一般来说,所谓“正确的类型”是指equals方法所在的那个类。 
3).把参数转换成正确的类型。因为转换之前进行过instanceof测试,所以确保会成功。 
4).对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部成功,则返回true;否则返回false。 
5).当编写完成了equals方法之后,检查“对称性”、“传递性”、“一致性”。

6.Java对象在JVM内存中的管理

JVM内存分为堆、栈和方法区三个区域,分别用于存储不同的数据。

2 堆区域介绍

(1)堆存储信息:

堆空间用于存储使用new关键字所创建的对象,见下图:

Animal animal = new Animal();

(2)延伸:对象的垃圾回收

对象被回收的时机:当一个对象没有任何引用时,被视为废弃的对象,属于被回收的范围。该对象中的所有成员变量也随之回收。

成员变量的生命周期:从对象在堆中创建开始到对象从堆中被回收结束。

(3)垃圾回收机制:

1)垃圾回收器(Garbage Collection, GC)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用指向的对象。

Java程序员不用担心内存管理,因为垃圾收集器会自动进行回收管理。

2)垃圾回收的方法(System.gc())介绍:

GC的回收对程序员来说是透明的,并不一定一发现有无引用的对象,就立刻回收;

一般情况下,当我们需要GC线程即刻回收无用对象时,可以调用System.gc()方法;

System.gc()用于建议虚拟机马上调度GC线程回收资源,具体的实现策略取决于不同的JVM系统。

(4)Java程序内存泄漏:

内存泄漏指,不再使用的内存没有被及时的回收。严重的内存泄漏会因过多的内存占用而导致程序的崩溃。

GC线程判断对象是否可以回收的依据时该对象是否有引用指向,因此,当确定该对象不再使用时,应该及时将其引用设置为null。

3 栈区域介绍

(1)栈存储信息:

栈空间用于存储程序运行时在方法中声明的所有局部变量。

Animal animal = new Animal();

int aa = 60;

(2)延伸:关于局部变量的栈空间和生命周期介绍

一个运行的Java程序从开始到结束会有多次方法的调用。JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为栈帧。

一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。当一个方法调用完成后,其对应的栈帧将被清除,局部变量无效。

(3)局部变量和成员变量基于内存空间等维度的比较:

1)局部变量:

定义在方法中

没有默认值,必须自行设定初始值

方法被调用时,存在栈中,方法调用结束后,从栈中清除;

2)成员变量:

定义在类中,方法外;

有默认初始值,可以不显示初始化;

3)所有类被实例化后,存在堆中,对象被回收时,成员变量失效;

4 方法区域介绍

(1)方法区存储信息:

方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储。

Animal animal = new Animal();

Animal类首先加载到JVM的方法区,其中包括Animal类的基本信息和方法定义。

(2)类和对象在内存空间中的关系:

当类的信息被加载到方法区时,除了类的类型信息以外,同时类的方法定义也被加载到方法区;

类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。

7.ArrayList和linkedList的区别

ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦
LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺点就是查找非常麻烦 要丛第一个索引开始
ArrayList和Vector都是用数组方式存储数据,此数组元素数要大于实际的存储空间以便进行元素增加和插入操作,他们都允许直接用序号索引元素,但是插入数据元素涉及到元素移动等内存操作,所以索引数据快而插入数据慢.
Vector使用了sychronized方法(线程安全),所以在性能上比ArrayList要差些.
LinkedList使用双向链表方式存储数据,按序号索引数据需要前向或后向遍历数据,所以索引数据慢,是插入数据时只需要记录前后项即可,所以插入的速度快.ArrayList是线程不安全的,不是同步的 

8.异常的体系,以及异常的父类

9.HashMap遍历方法,有序还是无序,以及HashMap插入头尾怎么做?

Map<String,String> map=new HashMap<String,String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");
        map.put("4", "value4");
        
        //第一种:普通使用,二次取值
        System.out.println("\n通过Map.keySet遍历key和value:");  
        for(String key:map.keySet())
        {
         System.out.println("Key: "+key+" Value: "+map.get(key));
        }
        
        //第二种
        System.out.println("\n通过Map.entrySet使用iterator遍历key和value: ");  
        Iterator map1it=map.entrySet().iterator();
        while(map1it.hasNext())
        {
         Map.Entry<String, String> entry=(Entry<String, String>) map1it.next();
         System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());
        }
        
        //第三种:推荐,尤其是容量大时  
        System.out.println("\n通过Map.entrySet遍历key和value");  
        for(Map.Entry<String, String> entry: map.entrySet())
        {
         System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());
        }
        
        //第四种  
        System.out.println("\n通过Map.values()遍历所有的value,但不能遍历key");  
        for(String v:map.values())
        {
         System.out.println("The value is "+v);
        }

      //使用List集合遍历HashMap

   HashMap<String, List> mapList = new HashMap<String, List>();

   for (Map.Entry entry : mapList.entrySet()) {

    String key = entry.getKey().toString();

    List<String> values = (List) entry.getValue();

    for (String value : values) {

    System.out.println(key + " --> " + value);

   }

 }

一般说Hashmap无序是因为存入的顺序和遍历出来结果的顺序不一致,出来的结果的顺序是按一定规则排序的,但是不是存入的顺序。

HashMap散列图、Hashtable散列表是按“有利于随机查找的散列(hash)的顺序”。并非按输入顺序。遍历时只能全部输出,而没有顺序。可以rehash()重新散列,来获得更利于随机存取的内部顺序。 总之,遍历HashMap或Hashtable时不要求顺序输出,即与顺序无关。

可以用java.util.LinkedHashMap 就是按加入时的顺序遍历了

 Map<String, String> paramMap = new LinkedHashMap <String, String>();

然后如果想要插入首或者尾的位置

建议使用TreeSet,建立TreeSet的时候指定一个comparator.就可以按照一定的顺序进行插入和遍历。

也可以将key的值按照一定编号进行命名,从而获取一定的顺序,也就可以按照该顺序进行插入指定的位置。

10.访问权限关键字,以及访问权限等级比较

修饰符
private:表示其后的元素除了类型的创建者和内部的方法之外任何人都不能访问。 

Java还有一种默认的访问权限,在不指定任何关键字的情况下,这种权限将发挥作用,它是一种包的访问权限,即在当前包中的任何元素可以访问。

protected:与private相当,差别在于继承的类可以访问protected修饰的元素,同包中的其他类元素也可以访问。 

public:表示其后的元素对任何人都可以使用。 

11.==可否用于比较对象,如果可以则是比较的对象的什么?

1)、==是一般用来比较值类型,比较两个数据类型的值是否相等,例如:byte,shot,char,int,long,float,double,boolean,值类型(还有对象引用)一般存储在内存的栈中 
2)、equals用来比较复合数据类型,复合数据类型的变量在栈中存储的是引用类型变量的地址,本身存储在堆中。 
当使用==比较复合数据类型时,比较的是他们在内存中的地址,使用同一个new出来的是相等,否则不相等。

比较实质: 
在JAVA中利用”==”比较变量时,系统使用变量在”栈”中所存的值作为比较的依据。 
基本数据类型在”栈”中存的是其内容值,而对象类型在”栈”中存的是地址,这些地址指向”堆”中的对象。 
java.lang包中的Object类有public boolean equals(Object obj)方法,它比较两个对象的内容是否相等。 
其它对象的equals方法仅当被比较的两个引用指向的对象内容相同时,对象的equals()方法返回true。 
总之,”==”和”!=”比较的是地址.

12.linkedlist线程安全吗?怎么处理的?有什么可以替换的?

不安全

解决办法:

1)、List<String> list = Collections.synchronizedList(new LinkedList<String>());

2)、LinkedList换成ConcurrentLinkedQueue     //建议使用

12.其他忘了很多,等想起来了再补充.……

多线程:

1.线程的创建方法有哪些?

  • 1、通过继承Thread类创建线程

    (1).首先定义一个类去继承Thread父类,重写父类中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。
    (2).直接创建一个ThreadTest类的对象,也可以利用多态性,变量声明为父类的类型。
    (3).调用start方法,线程启动,隐含的调用run()方法。

  • 2、通过实现Runnable接口创建线程

    (1).定义一个类实现Runnable接口,重写接口中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。
    (2).创建Runnable接口实现类的对象。
    (3).创建一个ThreadTest类的对象,需要封装前面Runnable接口实现类的对象。(接口可以实现多继承)
    (4).调用Thread对象的start()方法,启动线程

  • 3.通过Callable和Future创建线程

    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
    (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
    (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

  • 4.使用Executor框架来创建线程池。

    Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。

2.Runnable和Callable的区别是

    (1)Callable规定的方法是call(),Runnable规定的方法是run().
    (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
    (3)call方法可以抛出异常,run方法不可以
    (4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

计算机网络;

1.计算机网络分层体系结构

2.通信过程

14.TCP/IP是在那一层

框架部分:

spring:

15.首先问我了解深不深,然后问我有没有研究过源码;

16.问我配置方式是xml中配置还是使用注解,我回答的注解,然后就问我spring有哪些注解,dao城中用什么注解之类的

南望孤笑博主的博客中有这一篇对spring注解阐述的,我就把链接放这了。

springMVC:

17.用户发起请求后,整个处理机制。

具体步骤:

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)

第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)

第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)

第十一步:前端控制器向用户响应结果

总结:核心步骤

1、  DispatcherServlet 在 web.xml 中的部署描述,从而拦截请求到 Spring Web MVC

2、  HandlerMapping 的配置,从而将请求映射到处理器

3、  HandlerAdapter 的配置,从而支持多种类型的处理器

注:处理器映射求和适配器使用纾解的话包含在了注解驱动中,不需要在单独配置

4、  ViewResolver 的配置,从而将逻辑视图名解析为具体视图技术

5、  处理器(页面控制器)的配置,从而进行功能处理 

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

mybatis:

18.在mybatis中怎么处理防sql注入的

mybatis中的#和$的区别:

1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username="111", 如果传入的值是id,则解析成的sql为where username="id". 
2、$将传入的数据直接显示生成在sql中。
如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;
如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;
3、#方式能够很大程度防止sql注入,$方式无法防止Sql注入。
4、$方式一般用于传入数据库对象,例如传入表名.
5、一般能用#的就别用$,若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
6、在MyBatis中,“${xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。

mybatis是如何做到防止sql注入的

  MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

select id, username, password, role from user where username=? and password=?

  不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

  【底层实现原理】MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。

数据库部分:

1.事务隔离等级

事务隔离等级的博客,写得很详细

2.数据库必须具备的四个特性

1:原子性:事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响;

2:一致性:事务执行前和执行后必须处于一致性状态,例:用户A和用户B的前加起来一共是5000; 无论AB用户之间是如何相互转换的,事务结束后两个用户的钱加起来还是5000,这就是事务的一致性。

3:隔离性:当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不被其他事务的操作所干扰,多个并发事务之间要相互隔离;

4:持久性:一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便在数据库系统遇到故障的情况下也不会丢失事物的操作。

隔离性:当多个线程都开启事务来操作数据库中的数据时,数据库系统要进行隔离操作,以保证各个线程获取数据的准确性。

不考虑事务的隔离性,会产生的几种问题:

01:脏读

是指一个事务处理过程里读取了另一个未提交的事务中的数据,然后使用了这个数据;
例:用户A向用户B转账100元,A通知B查看账户,B发现前确实已到账,而之后无论第二条SQL是否执行,只要该事务部提交,则所有操作都将回滚,当B再次查看账户时就会发现前其实并没有到账。

02:不可重复读
不可重复读是指在一个事务内,多次读取同一个数据,在这个事务还没有结束 ,另一个事务也访问该同一数据,但是由于第二个事务的修改,那么第一个事务两次读取的数据可能不一样,因此称为不可重复读;即同一个事务中原始数据读取不可重复。 注:不可重复读和脏读的区别,脏读是某一个事务读取另一个事务未提交的脏数据; 不可重复读则是读取前一事务提

03:幻读:
当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行修改,这种数据涉及到表中的全部数据行,同时,第二个事务也对这个表数据进行修改,这个修改是对表中新增/删除一条数据,那么操作第一个事务的用户发现表中的数据还没有修改的数据行,就好像发生了幻觉一样,这就是发生了幻读。
注:幻读和不可重复读都读取另一条已经提交的事务,所不同的是不可重复读查询的都是同一数据项,而幻读针对的是一批数据整体。

其他:

20.学习一项新的技术是怎么学习的

转载自: https://blog.csdn.net/qq_36927265/article/details/81975545

猜你喜欢

转载自blog.csdn.net/weixin_41969587/article/details/88271385