大数据面试之一(java)

第1章 Java
1.1 ConcurrentHashMap 是怎么实现的?
答:ConcurrentHashMap 是concurrent 包中线程安全的哈希表,采用分段锁,可以理解为把一个大的 Map 拆分成 N 个小的 HashTable,根据 key.hashCode() 来决定把 key 放到哪个 HashTable 中。在 ConcurrentHashMap 中,就是把 Map 分成了 N 个 Segment,put 和 get 的时候,都是现根据 key.hashCode() 算出放到哪个 Segment 中。
1.2 List 与 set 的区别
答:List 和 Set 都是接口。他们各自有自己的实现类,有无顺序的实现类,也有有顺序的实现类。 最大的不同就是 List 是可以重复的。而Set是不能重复的。 List 适合经常追加数据,插入,删除数据。但随即取数效率比较低。 Set 适合经常地随即储存,插入,删除。但是在遍历时效率比较低。
1.3 工厂模式
答:工厂模式一般分为三种: 简单工厂模式、工厂方法模式、抽象工厂模式
工厂模式:简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。
1.4 多线程有几种创建方式?
一般有四种方法,
(1)继承Thread,
(2)实现Runnable接口,
(3)实现Callable接口,
(4)使用Executor框架来创建线程池。
1.2 Java的特性
1、抽象
父类为子类提供一些属性和行为,子类根据业务需求实现具体的行为。抽象类使用abstract进行修饰,子类要实现所有的父类抽象方法否则子类也是抽象类。
面向对象有三个主要特征,分别是封装性、继承性和多态性。
2、封装
类的说明展现了封装性,类作为对象的模板,含有私有数据和公有数据,封装性能使数据更加安全,依赖的就是类的特性,使得用户只能看到对象的外在特性,不能看到对象的内在属性,用户只能访问公有数据不能直接访问到私有数据。
3、继承
类的派生功能展现了继承性,继承性是子类共享父类的机制,但是由于封装性,继承性也只限于公有数据的继承(还有保护数据的继承),子类在继承的同时还可以进行派生。
4、多态
多态是指父对象中的同一个行为能在其多个子对象中有不同的表现。也就是说子对象可以使用重写父对象中的行为,使其拥有不同于父对象和其它子对象的表现,这就是overriding(重写)。overload(重载)不属于面向对象中的多态的范畴。
多态存在的三个必要条件:要有继承、要有重写、父类引用指向子类对象。
1.3 设计模式
1.设计模式是什么,设计模式有什么作用?
设计模式是一套被反复使用的、多数人知晓、经过分类编目的优秀代码设计经验的总结。特定环境下特定问题的处理方法。
1)重用设计和代码 重用设计比重用代码更有意义,自动带来代码重用
2)提高扩展性 大量使用面向接口编程,预留扩展插槽,新的功能或特性很容易加入到系统中来
3)提高灵活性 通过组合提高灵活性,可允许代码修改平稳发生,对一处修改不会波及到其他模块
4)提高开发效率 正确使用设计模式,可以节省大量的时间
2.开发中用过到了哪些设计模式,用在什么场合?
a) 单例模式:单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。
b) 策略模式:就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。

c) 工厂模式:简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。
1.5 tcp,udp的区别
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,是专门为了在不可靠的网络中提供一个可靠的端对端字节流而设计的,面向字节流。
UDP(用户数据报协议)是iso参考模型中一种无连接的传输层协议,提供简单不可靠的非连接传输层服务,面向报文
区别:
1) TCP是面向连接的,可靠性高;UDP是基于非连接的,可靠性低
2) 由于TCP是连接的通信,需要有三次握手、重新确认等连接过程,会有延时,实时性差,同时过程复杂,也使其易于攻击;UDP没有建立连接的过程,因而实时性较强,也稍安全
3) 在传输相同大小的数据时,TCP首部开销20字节;UDP首部开销8字节,TCP报头比UDP复杂,故实际包含的用户数据较少。TCP在IP协议的基础上添加了序号机制、确认机制、超时重传机制等,保证了传输的可靠性,不会出现丢包或乱序,而UDP有丢包,故TCP开销大,UDP开销较小
4) 每条TCP连接只能是点到点的;UDP支持一对一、一对多、多对一、多对多的交互通信
应用场景选择
对实时性要求高和高速传输的场合下使用UDP;在可靠性要求低,追求效率的情况下使用UDP;
需要传输大量数据且对可靠性要求高的情况下使用TCP
1.6 网络七层和网络四层,它们的区别是什么
“七层”是OSI参考模型,即物理层 、 数据链路层 、 网络层、传输层、 会话层 、表示层、应用层 ;
“四层”是TCP/IP参考模型,即物理链路层、 网络层、传输层、应用层。
虽说有四层和七层之说,但是其实一样的,TCP/IP中的物理链路层对应OSI中的物理层和数据链路层 ,网络层对应网络层,传输层对应传输层,应用层对应会话层 、表示层、应用层 。
总结
网络七层模型是一个标准,而非实现。
网络四层模型是一个实现的应用模型。
网络四层模型由七层模型简化合并而来
1.7 用java写出单例模式(多种方法加分)
答:单例模式主要作用是保证在Java应用程序中,一个类只有一个实例存在。下面给出两种不同形式的单例:
第一种形式:饿汉式单例
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式。但是问题也比较明显。单例在还没有使用到的时候,初始化就已经完成了。也就是说,如果程序从头到位都没用使用这个单例的话,单例的对象还是会创建。这就造成了不必要的资源浪费。所以不推荐这种实现方式。
第二种形式:懒汉式单例(线程不安全)
public class Singleton {
//类初始化时,不会初始化这个对象(延迟加载,真正用的时候再创建)
private static Singleton instance = null;
//构造方法私有化
private Singleton() {}
//方法同步,调用效率低
public static synchronized Singleton getInstance(){
if (instance==null) instance=newSingleton();
return instance;
}
}

加了synchronized线程才安全,调用效率不高,但是能延迟加载
单例的特点:外界无法通过构造器来创建对象,该类必须提供一个静态方法向外界提供该类的唯一实例。
【补充】用Java进行服务器端编程时,使用单例模式的机会还是很多的,服务器上的资源都是很宝贵的,对于那些无状态的对象其实都可以单例化或者静态化(在内存中仅有唯一拷贝),如果使用了spring这样的框架来进行对象托管,Spring的IoC容器在默认情况下对所有托管对象都是进行了单例化处理的。
1.8 输入字符串输出判断输入是否是一个数字
例如:3.5->true
Ooo->false;
代码:
import java.util.Scanner;
public class hasNextInt的判断 {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
String num;
if(input.hasNextInt()){
num=input.next();
System.out.println(“是数字”);
}else {
num=input.next();
System.out.println(“不是数字”);
}
}
}
1.9 Java中单例模式有哪几种?具体哪个线程安全不安全的怎么处理? 同步锁怎么加 锁在什么时候会释放 ?
饿汉式 懒汉式。其中,懒汉式是线程不安全的,当有多条线程同时访问单例对象时,则会出现多线程临界资源问题。一个解决办法就是加锁。
1.10 == equals的区别
1、初步认识equals与==的区别
(1)==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
(2)==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
(3)==指引用是否相同, equals()指的是值是否相同
用一张图可以简要的表现他们之间的关系

2、进一步了解
(1)==是一个运算符,它比较的是值
对于基本数据类型,直接比较其数据值是否相等。如果是不同的基本数据类型之间进行比较,则遵循基本数据类型间运算的转换原则(见上面总结的第二条)。如下:
if(12 == 12.0){
System.out.println("-----12 == 12.0-------");
}
此时打印了-----12 == 12.0-------,因为低一级的int类型的12自动转换为高一级的float类型

对于引用类型,比较的还是值,只不过此时比较的是两个对象变量的内存地址。所以,用来比较对象,实际上是判断这两个对象是否是同一个new出来的对象,或者是否是一个对象赋值给另一个对象的情况。如:String s1 = new String(“abc”);
String s2 = s1;//将s1对的内存地址赋给了s2,此时s1s2返回true;
(2)equals
equals方法是属于Object类的一个方法,其实现源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,其实equals方法里面用的还是
运算符,所以对于那些没有重写过Object类的equals方法来说,==和equals方法是等价的!然而,很多类都自己去重写了equals方法,比如String类、所有基本数据类型的包装类等
String类的equals源码如下:、

首先判断是否是同一个new出来的对象,即判断内存地址是否相同;如果不同则判断对象中的内容是否相同。
Integer类的equals方法如下:

直接转成判断值是否相等了。因此,对于String类和所有基本数据类型的包装类来说,equals方法就是判断其内容是否相等。对于其他类来说,要具体看其是否重写了equals方法及具体业务实现。另:对于基本数据类型来说,使用equals方法,需要用该基本类型对应的包装类,因为equals是针对对象来使用的!
1.11 Java线程的理解
1、什么是线程?
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。

2、线程和进程有什么区别?
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

3、如何在Java中实现线程?
1)java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,
2)由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。
3).实现Callable接口通过FutureTask包装器来创建Thread线程
1.12 对Spring框架的理解
spring 是按照设计模式精心打造的,它实现了工厂模式的工厂类,这个类名为BeanFactory(接口),在程序中通常使用它的子类ApplicationContext(也是接口)。
spring的核心是IOC(反转控制)容器,IOC也是一种编程思想,用于实现模块之间的解耦,在Spring中它的作用是对对象的创建,维护和销毁等生命周期的控制。IOC:把对象的创建、初始化、销毁交给spring来管理,而不是由开发者控制,实现控制反转。
spring是一个大的工厂类,spring的特点就是基于配置,在其配置文件中通过元素来创建实例对象。
根据业务逻辑来看,对象经常不是独立的,一个对象的创建往往涉及另一个对象的创建,当然这个对象也要由IOC容器负责,负责的方式就是依赖注入DI,通过反射机制实现。有三种注入方式:(1)接口注入(2)构造器注入(3)Setter方法注入。
1.13 Java中的数据结构,ArrayList和LinkedList的区别,ArrayList为什么查询快(为什么用数组就快)、LinkedList为什么增删快?
1、ArrayList是基于动态数组的集合。LinkedList是基于链表的集合。
2、ArrayList允许保存所有元素,包括null,并可以根据索引位置对集合进行快速访问;对于随机访问集合中的对象,使用LinkedList类实现List集合的效率较低。
3、ArrayList向集合中指定位置插入对象或者删除元素的速度较慢;LinkedList需要向集合中插入和删除对象时,使用LinkedList集合的效率较高。
1.14 哪些Map是线程安全的
JAVA中线程安全的map有:Hashtable、synchronizedMap、ConcurrentHashMap。
1.15 Java的NIO是否了解
Java NIO(Java non-blocking IO)是JDK1.4以后推出的,相对于原来的IO来说,Java NIO是一种非阻塞的IO方式,它为所有的基本类型提供了缓存支持。

1)阻塞和非阻塞:
 阻塞:当某个事件或者任务在执行过程中,它发出一个请求操作,但是由于该请求操作需要的条件不满足,那么就会一直在那等待,直至条件满足;
非阻塞:当某个事件或者任务在执行过程中,它发出一个请求操作,如果该请求操作需要的条件不满足,会立即返回一个标志信息告知条件不满足,不会一直在那等待。
这就是阻塞和非阻塞的区别。也就是说阻塞和非阻塞的区别关键在于当发出请求一个操作时,如果条件不满足,是会一直等待还是返回一个标志信息。
2)Java NIO中比较核心的三个概念是:通道(Channel)、缓冲区(Buffer)、选择器(Selector)。
(1)通道(Channel):通道和传统IO中的Stream类似,但传统IO中的Stream是单向的,比如InputStream只能进行读(read)操作,OutputStream只能进行写(write)操作,而NIO中的通道是双向的,既可以进行读也可以进行写操作。
(2)缓冲区(Buffer)
Buffer实际上是一个容器,是一个连续数组。Channel提供从文件、网络读取数据的渠道,使用Java NIO时,所有数据处理(读取或写入)必须经由Buffer。即读入数据时,先通过通道Channel获取数据渠道,然后将数据写入到缓冲区Buffer中,往外写数据时,先把内存中的数据放到Buffer中,然后再从Buffer送往通道中。如下图所示:

(3)选择器(Selector)
Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。
1.16 jvm结构?堆里面几个区?
1、JVM内存区域分为程序计数器、Java栈、Java堆、本地方法栈、方法区。
(1)程序计数器:是最小的一块内存区域,每个线程都有一个程序计数器,是线程私有的。它的作用是当前线程所执行的字节码的行号指示器。
(2)堆:也叫做java 堆、GC堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,在JVM启动时创建。

(3)虚拟机栈:描述的是Java 方法执行的内存模型:每个方法被执行的时候 都会创建一个“栈帧”用于存储局部变量表(包括参数)、操作栈、方法出口等信息。
(4)方法区:也称"永久代” 、“非堆”, 它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域
(5)本地方法栈:与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务

2、堆:
JVM中共享数据空间可以分成三个大区,新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation),其中JVM堆分为新生代和老年代,逻辑上永久代(方法区)属于堆;
(1) 新生代可以划分为三个区,Eden区(存放新生对象),两个幸存区(From Survivor和To Survivor)(存放每次垃圾回收后存活的对象);
(2)永久代管理class文件、静态对象、属性等(JVM uses a separate region of memory, called the Permanent Generation (orPermGen for short), to hold internal representations of java classes. PermGen is also used to store more information );d.JVM垃圾回收机制采用“分代收集”:新生代采用复制算法,老年代采用标记清理算法。

注:JDK1.8之后将最初的永久代取消了,由元空间取代。
1.17 JVM中到底哪些区域是共享的?哪些是私有的?
答:Java Heap(堆)和Method Area是共享的,其他都是私有的,

1.18 Java支持的数据类型有哪些?什么是自动拆装箱?
1)Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。装箱是指,将基本数据转化成对象。转化为对象可以方便的操作数据。利用面向对象的方法方便的实现对数据的操作。拆箱就是装箱的逆过程。

Java 为每个原始类型提供了包装类型:
基本数据类型 长度 包装类型
byte(字节) 8位 Byte
short(短整型)   16位 Short
int(整型)   32位 Integer
long(长整型) 64位 Long
float(浮点型) 32位 Float
double(双精度) 64位 Double
char(字符型)   16位 Character
boolean(布尔型) 1位 Boolean

1.21 什么是死锁(deadlock),如何确保N个线程可以访问N个资源同时又不导致死锁?
什么是死锁?所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
使用多线程时,一种非常简单的避免死锁的方式就是:指定锁的顺序,并强制线程按照指定的顺序获取锁。因此所有的线程都是以同样的加锁和释放锁,就不会出现死锁了。
1.22 Java中HashMap的工作原理是什么?
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMap和ConcurrentHashMap。
1.23 关于垃圾回收问题
1、你知道哪些垃圾回收算法?
垃圾回收从理论上非常容易理解,具体的方法有以下几种:

  1. 标记-清除
  2. 标记-复制
  3. 标记-整理
  4. 分代回收

2、如何判断一个对象是否应该被回收

这就是所谓的对象存活性判断,常用的方法有两种:

1.引用计数法;

2.对象可达性分析。由于引用计数法存在互相引用导致无法进行GC的问题,所以目前JVM虚拟机多使用对象可达性分析算法。

3、简单的解释一下垃圾回收

Java 垃圾回收机制最基本的做法是分代回收。内存中的区域被划分成不同的世代,对象根据其存活的时间被保存在对应世代的区域中。一般的实现是划分成3个世代:新生代、老年代和永久代。内存的分配是发生在新生代中的。当一个对象存活时间足够长的时候,它就会被复制到老年代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于新生代的垃圾回收算法就可以很有针对性。

4、调用System.gc()会发生什么?
  通知GC开始工作,但是GC真正开始的时间不确定。
1.25 Spring中有哪些不同的IOC(依赖注入)方式?
补充:什么是spring的依赖注入?
依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。

构造器依赖注入: 构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter方法注入: Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
1.27 Spring框架中的单例bean是线程安全的吗?为什么?
实际上,大部分的 Spring bean 并没有可变的状态 ( 比如 Service 类和 DAO 类 ) ,所以在某种程度上说 Spring 的单例 bean 是线程安全的 。 如果你的 bean 有多种状态的话(比如 View Model 对象),就需要自行保证线程安全 。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值