大家都知道,solr是一个搜索引擎,先利用分词器建立倒排索引,在对索引进行搜索,但是solr自带的分词器对于中文不是很好用,所以在对中文进行分词的情况中,常用的分词器是IK分词工具。
这里顺便提一个问题,虽然这个概念没被问到,但是学solr的时候已经见到过无数次了。
数据可以分为两类,结构化数据和非结构化数据,那啥是结构化数据、啥又是非结构化数据?
由于我还没怎么深入学习Spring框架,所以这里问了一些简单的IOC、DI、AOP的相关问题就过去了。(是得好好看看Spring了)
HashMap采取数组加链表的存储方式来实现。亦即数组(散列桶)中的每一个元素都是链表,如下图:
为什么需要有这些长度不一的链表呢?
首先我们需要知道什么是哈希冲突。
其实就是再采用哈希函数对输入域进行映射到哈希表的时候,因为哈希表的位桶的数目远小于输入域的关键字的个数,所以,对于输入域的关键字来说,很可能会产生这样一种情况,也就是,不同的关键字会映射到同一个位桶中的情况,这种情况就就叫做哈希冲突。
一般来说,解决哈希冲突有四种方法:拉链法(链地址法)、开放地址法、再哈希法和建立公共溢出区(如果说解决哈希冲突的三种方法,就说前三种),这里只稍微介绍一下,暂时不细说。
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
我们能否让HashMap同步?
答案是肯定的,HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
结论
Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。
(以上内容引用自博客)
知识盲区,很重要,得细看,先把简单的处理掉
基础中的基础,必备知识点,网上答案很多,我从博客中盗来一张图,感觉这个说得比较清楚。
类型的简单对比
基本数据类型 | 引用数据类型 |
---|---|
在栈中进行分配 | 在堆中进行分配,堆的读写速度远不及栈 |
变量名指向具体的数值 | 变量名指向存数据对象的内存地址,即变量名指向hash值 |
变量在声明之后java就会立刻分配给他内存空间 | 它以特殊的方式(类似C指针)指向对象实体(具体的值),这类变量声明时不会分配内存,只是存储了一个内存地址 |
基本类型之间的赋值是创建新的拷贝 | 对象之间的赋值只是传递引用 |
“==”和“!=”是在比较值 | “==”和“!=”是在比较两个引用是否相同,值比较需要自己实现equals()方法 |
基本类型变量创建和销毁很快 | 类对象需要JVM去销毁 |
(该表格引用自博客)
这个问题在之前的文章里总结过,这里就不再细说了,看这篇《ALi面试题》。
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。
解决办法:在修改数据库的同时修改缓存器中的数据。(这是一个比较笼统的答案,应该还有比较细致、精确的答案,但我还没找到)
(下面的缓存击穿及解决办法的相关内容主要来自博客)
什么是缓存击穿:在高并发下,多线程同时查询同一个资源,如果缓存中没有这个资源,那么这些线程都会去数据库查找,对数据库造成极大压力,缓存失去存在的意义。
解决办法:
封装:
概念:
将类的某些信息隐藏在类的内部,不允许外部程序访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
好处:
只能通过规定的方法访问数据
隐藏类的实例细节,方便修改和实现
继承:
继承是类与类的一种关系,例如:动物和狗的关系,动物是父类(或基类),狗是子类(或派生类)。
要注意的是,在Java中的继承是单继承,也就是说一个儿子只能有一个爸爸
继承的好处:
子类拥有父类的所有属性和方法(private除外)
子类对父类代码的复用
提到继承,特别注意重载和重写的关系
继承的初始化顺序:
若创建一个子类对象,系统会先创建父类的属性进行初始化,再调用父类的构造方法,然后再创建子类的属性进行初始化,最后调用子类的构造方法。
多态:
多态指对象的多种引用形态,继承是多态的前提
父类的引用可以指向本类对象 Animal object1=new Animal();
父类的引用可以指向子类对象 Animal object2=new Dog();
注意:子类的引用不可以指向父类对象Dog object3=new Animal();
创建本类对象时,调用的方法为本类的方法;
创建子类对象时,调用的为方法为子类重写的方法或者继承的方法
注意:本类对象不能调用子类的方法
引用类型转换:
Dog dog=new Dog();
Animal animal=dog(); //向上类型转换:(不存在风险)
Dog dog2=(Dog)animal; //向下类型转换:(存在风险,可能出现数据溢出)
if(animal instenceof Cat){ //用instanceof运算符,来解决引用对象的类型,避免类型转换的安全问题,返回布尔值,来判断animal能否转换为Cat类型
Cat cat=(Cat)animal;
}
(参考博客1和博客2,还有我自己的这篇文章)
从上到下分别为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。
线程是一种轻量级的进程,与进程相比,线程给操作系统带来侧创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。
线程没有地址空间,线程包含在进程的地址空间中。线程上下文只包含一个堆栈、一个寄存器、一个优先权,线程文本包含在他的进程 的文本片段中,进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段, 寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。
父和子进程使用进程间通信机制,同一进程的线程通过读取和写入数据到进程变量来通信。
进程内的任何线程都被看做是同位体,且处于相同的级别。不管是哪个线程创建了哪一个线程,进程内的任何线程都可以销毁、挂起、恢复和更改其它线程的优先权。线程也要对进程施加控制,进程中任何线程都可以通过销毁主线程来销毁进程,销毁主线程将导致该进程的销毁,对主线程的修改可能影响所有的线程。
子进程不对任何其他子进程施加控制,进程的线程可以对同一进程的其它线程施加控制。子进程不能对父进程施加控制,进程中所有线程都可以对主线程施加控制。
(内容来自百家号)