java&hadoop面试总结(实习生)

一、hadoop中mapreduce提交作业的工作流程

 1.client中,客户端提交一个mapreduce(下文都用mr代替)的jar包给JobClient。

2.JobClient持有ResourceManager的一个代理对象,它向ResourceManager发送一个RPC请求,告诉ResourceManager作业开始,然后ResourceManager返回一个JobID和一个存放jar包的路径给Client。


3.Client将得到的jar包的路径作为前缀,JobID作为后缀(path = hdfs上的地址 + jobId) 拼接成一个新的hdfs的路径,然后Client通过FileSystem向hdfs中存放jar包,默认存放10份(NameNode和DateNode等操作)。


4.开始提交任务,Client将作业的描述信息(JobID和拼接后的存放jar包的路径等)RPC返回给ResourceManager。


5.ResourceManager进行初始化任务,然后放到一个调度器中。

6.ResourceManager读取HDFS上的要处理的文件,开始计算输入分片,每一个分片对应一个MapperTask,根据数据量确定起多少个mapper,多少个reducer。


7.NodeManager 通过心跳机制向ResourceManager领取任务(任务的描述信息)

8.领取到任务的NodeManager去Hdfs上下载jar包,配置文件等

9.NodeManager启动相应的子进程yarnchild,运行mapreduce,运行maptask或者reducetask

10.map从hdfs中读取数据,然后传给reduce,reduce将输出的数据给回hdfs

二、抽象类和接口的区别

抽象类的特点:

1、抽象类和抽象方法必须用abstract关键字修饰

2、抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类

3、抽象类不能实例化。(因为它不是具体的,抽象类有构造方法,但不能实例化,构造方法的作用是用于子类访问父类数据的初始化)

4、抽象类的子类(a.如果不想重写抽象方法的话,该子类是一个抽象类,重写全部的抽象方法的话,该子类是一个具体类)

        抽象类的实例化是靠着具体子类实现的,是多态的方式

      如: Animal a = new Cat();

抽象类的成员特点:

              成员变量:可以是变量也可以是常量。

              构造方法:有构造方法。

              成员方法:既可以是抽象的也可以是非抽象的。

接口的特点:

1.用关键字Interface来定义(       interface 接口名{}     )

2.类实现接口用implements关键字(     class    类名     implements    接口名{}      )

3.接口也是抽象的,不能实例化(也是用多态的方式来实例化,如:Animalinterface at = new Catinterface();

接口的成员特点

             成员变量:只能是常量,并且是静态的。默认修饰符:public static final

             构造方法:接口没有构造方法。

             成员方法:只能是抽象方法。

两者区别:

A:成员区别
         抽象类:
                 成员变量:可以有变量,也可以有常量
                 构造方法:有
                 成员方法:可以抽象,也可以非抽象
         接口:
                 成员变量:只可以常量
                 构造方法:无
                 成员方法:只可以抽象
 
 B:关系区别:
         类与类:
             继承,单继承
         类与接口:
             实现,单实现,多实现
         接口与接口:
             继承,单继承,多继承
 
 C:设计理念不同
         抽象类 被继承体现的是:“is a”的关系。抽象类中定义的是该继承体系的共性功能。
         接口 被实现体现的是:“like a”的关系。接口中定义的是该继承体系的扩展功能。

三、final,finally、finalize关键字的区别和用法

final:最终的意思,常见的是它可以修饰类,方法,变量。
          特点:
              final可以修饰类,该类不能被继承。
              final可以修饰方法,该方法不能被重写。
              final可以修饰变量,该变量不能被重新赋值,因为这个变量其实是常量。

final修饰局部变量的问题
 
         基本类型:基本类型的值不能发生改变
         引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。

finally:

       是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获都会被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,可以放在finally块中。

try…catch…finally结构的语法格式

try

{

  可能会出现异常的代码段

}

Catch(异常类名 处理该异常对象)

{

  异常处理代码段

}

Finally

{

总是需要执行的代码段

}

finalize

       finalize是方法名,java技术允许使用finalize()方法在垃圾收集器将对象从内存中清楚出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

四、HashMap和Hashtable的区别

Hashmap和Hashtable:底层数据结构是哈希表。

                   哈希表依赖的是两个方法:hashCode()和equals()

                   执行顺序:首先判断hashCode()值是否相同

                                    如果是的话,就继续比较equals(),看它的返回值

                                                                如果是true,说明元素重复,不添加;如果是false,说明元素不重复,添加。

                                     如果hashCode()值不相同,就直接添加。

hashmap:是线程不安全,效率高;hashtable:是线程安全,效率低。

五、hasmap底层实现(用的是什么数据结构、如何通过key值查询到value)

HashMap和Hashtable的底层实现都是数组+链表结构实现的


这里转载别人的比较详细,https://blog.csdn.net/u011202334/article/details/51496381


六、反射机制是什么?

Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制。

Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了 Field,Method,Constructor 类 (每个类都实现了 Member 接口)。这些类型的对象时由 JVM 在运行时创建的,用以表示未知类里对应的成员。

这样你就可以使用 Constructor 创建新的对象,用 get() 和 set() 方法读取和修改与 Field 对象关联的字段,用 invoke() 方法调用与 Method 对象关联的方法。另外,还可以调用 getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。

在 Java 中可以通过三种方法获取类的字节码 (Class) 对象

  • 通过 Object 类中的 getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象。
  • 所有数据类型都具备一个静态的属性.class 来获取对应的 Class 对象。但是还是要明确到类,然后才能调用类中的静态成员。
  • 只要通过给定类的字符串名称就可以获取该类的字节码对象,这样做扩展性更强。通过 Class.forName() 方法完成,必须要指定类的全限定名,由于前两种方法都是在知道该类的情况下获取该类的字节码对象,因此不会有异常,但是 Class.forName() 方法如果写错类的路径会报 ClassNotFoundException 的异常。

七、多线程为什么加锁?

  1. 多线程、进程并行访问共享资源时,一定要加锁保护 
  2. 锁的职责单一 
  3. 锁范围尽量小,只锁对应资源操作代码
  4. 避免嵌套加锁;如果必须加锁,务必保证不同地方的加锁顺序是一样的

详见:https://blog.csdn.net/qq_35475767/article/details/54378363

如何加锁?

1. synchronized关键字

2. Java.util.concurrent包中的lock接口和ReentrantLock实现类

详见:https://blog.csdn.net/u010842515/article/details/67634813

 

八、wait、yield、sleep的区别?

sleep()与wait()的区别

注:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
    而Object代表任意的对象,所以,定义在这里面。

  1. 这两个方法来自不同的类,sleep是Thread类的方法,而wait是Object类的方法;
  2. 执行sleep方法后不会释放锁,而执行wait方法后会释放锁;
  3. wait,notify和notifyAll只能在同步方法或同步代码块中调用,而sleep可以在任何地方调用;
  4. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。(如果不是在同步方法或同步代码块中调用wait()方法,则抛出IllegalMOnitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch语句进行捕捉异常)
  5.  sleep():必须指定时间
     wait():可以不指定时间,也可以指定时间

需要注意以下几点:
1. 在执行notify()或notifyAll()方法后,当前线程不会马上释放该对象锁,需要等到notify()或notifyAll()方法所在的同步方法或同步代码块执行完成,当前线程才会释放锁。
2. 在sleep()状态下interrupt()中断线程,会进入catch语句,并且清除停止状态值,使之变成false。
3. wait(long)方法:如果线程在指定时间(long)内未被唤醒,则自动唤醒。wait(0)等价于wait()。
4. Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。

sleep()与 yield()的区别

  1. sleep()方法给其他线程运行机会时不考虑其他线程的优先级,因此会给低优先级的线程运行的机会yield()方法只会给相同优先级或更高优先级的线程运行的机会
  2. 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态
  3. sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明任何异常
  4. sleep()方法比yield()方法具有更好的可移植性(跟操作系统CPU调度相关)。
  5. sleep方法需要参数,而yield方法不需要参数。

转载关于wait、sleep、yield的区别


https://blog.csdn.net/gonelikefly/article/details/77895864


 

九、equals和== 的区别

==:如果比较的是基本数据类型,比较的是数据值;

        如果比较的是引用数据类型,比较的是地址值;

equals:如果重写了equals方法的话,比较的是数据值,如果没有重写equals方法的话,比较的是地址值。

十、用堆排序来实现一个无序数组的的排序,用代码形式写出来。

 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。(摘自百度百科)
  
  想知道什么是堆排序,就得先知道什么是堆,堆分为两种,大根堆和小根堆,什么是大根堆小根堆呢?那你得先知道完全二叉树,什么是完全二叉树?完全二叉树,若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。要是问什么是二叉树(自己去百度吧……),把完全二叉树理解了之后,就可以看大根堆小根堆了,大根堆的要求是每个节点的值都不大于其父节点的值,小根堆则相反,所以说,如果我构造出来一个大根堆,那么最上面的根节点一定是最大的
  
  堆排序的原理就是这样,先构造出来大根堆(假设从小到大排序),然后取出堆顶元素(也就是最大的元素),放到数组的最后面,然后再将剩余的元素构造大根堆,再取出堆顶元素放到数组倒数第二个位置,依次类推,知道所有的元素都放到数组中,排序就完成了,仔细看代码吧,光看字是很难懂的
  
  下面是Java代码实现:
  

class Demo
{
    public static void main(String[] args)
    {
        //定义整型数组
        int[] arr = {1,5,6,8,7,2,3,4,9};
        //调用堆排序数组
        HeapSort(arr);
        //输出排序后的数组
        for(int i=0;i=0;i--)
        {
            //构造大顶堆,从下往上构造
            //i为最后一个根节点,n为数组最后一个元素的下标
            HeapAdjust(arr,i,n);
        }
        for(int i=n;i>0;i--)
        {
            //把最大的数,也就是顶放到最后
            //i每次减一,因为要放的位置每次都不是固定的
            swap(arr,i);
            //再构造大顶堆
            HeapAdjust(arr,0,i-1);
        }
    }

    //构造大顶堆函数,parent为父节点,length为数组最后一个元素的下标
    public static void HeapAdjust(int[] arr,int parent,int length)
    {
        //定义临时变量存储父节点中的数据,防止被覆盖
        int temp = arr[parent];
        //2*parent+1是其左孩子节点
        for(int i=parent*2+1;i<=length;i=i*2+1)
        {
            //如果左孩子大于右孩子,就让i指向右孩子
            if(i=arr[i])
            {
                break;
            }
            //如果父节点小于孩子节点,那就把孩子节点放到父节点上
            arr[parent] = arr[i];
            //把孩子节点的下标赋值给parent
            //让其继续循环以保证大根堆构造正确
            parent = i;
        }
        //将刚刚的父节点中的数据赋值给新位置
        arr[parent] = temp;
    }

    //定义swap函数
    //功能:将跟元素与最后位置的元素交换
    //注意这里的最后是相对最后,是在变化的
    public static void swap(int[] arr,int i)
    {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
    }
}

堆排序,详见https://blog.csdn.net/qq_40086556/article/details/81840315


 

十一、linux常用命令之top是用来干什么的?查看进程用哪个命令?

top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,常用于服务端性能分析。

下面的链接是转载比较详细的top命令的使用说明。


https://blog.csdn.net/bbirdsky/article/details/52085633


查看进程命令用 ps

ps -aux     查看系统所有进程

ps -lA        查看所有系统的数据

下面的链接是转载比较详细的查看进程命令的总结


https://blog.csdn.net/longerzone/article/details/8015941


 

你可能感兴趣的:(面试总结)