1.00 什么时候使用基于接口编程?
基于接口编程、Fascade层等等抽象封装都是有开发和维护的代价的,是否使用归根结底还是要看团队人员的分工情况,
技术方面确实需要时,比如不同开发语言下连接;手机app与服务系统连接等,自然就要用了。
1.01 Package是先分层还是先分模块?
org.springside.模块A.web 还是 org.springside.web.模块A? 同上,还是看团队人员的分工情况。
如果是每人从头到尾负责一个独立模块的可以先分模块,反之,按层进行分工并注重层内重用的,可以考虑先分层。
1.02 怎么处理日志问题?有那些可行的方案?
首先要定义一个项目的异常处理类,然后所有需要处理异常的类就用该类来处理。日志的操作和处理就在该类中操作;
方案一:在service层的try catch中设置错误日志打印;
方案二:在filter拦截器中统一设置错误日志
1.03 反射机制
反射机制,就是当不确定类的类型时,采用java.lang.reflect方式定义转换类的类型;
反射的功能很强大,但是使用不当可能会缺点大于优点,反射使代码逻辑混乱,会带来维护的问题。众所周知Java有个Object class,是所有Java classes的继承根源。
1.04 面向对象理解
什么是OOP?什么是类?请对比类和对象实例之间的关系。OOP是面向对象编程缩写。指的是用对象的观点来组织与构建系统,它综合了功能抽象和数据抽象,这样可以减少数据之间的耦合性和代码的出错几率。使用面向对象编程技术可以使得软件开发者按照现实世界里人们思考问题的模式编写代码,可以让软件开发者更好地利用代码直接表达现实中存在的对象;类是同一类对象实例的共性的抽象,对象是类的实例化。对象通常作为计算机模拟思维,表示真实世界的抽象。类是静态的,对象是动态的,类负责产生对象,可以将类当成生产对象的工厂。
1.05 基本数据类型
1、Java的数据类型可以划分为4大类:整数,浮点数,字符型,布尔型。其中整数可以划分为:byte,short,int,long.浮点数可以划分为float,double。
2、char型变量中能不能存贮一个中文汉字?为什么?
答:是能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的。
1.06 String与StringBuffer的区别。
String的长度是不可变的,StringBuffer的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法。
1.07数据类型大小及取值范围
大小:byte 1个字节、short 2个字节、int 4个字节、long 8个字节;char 2个字节;float 4个字节、double 8个字节;
取值范围:
byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1)的取值范围为-32768~32767,占用2个字节(-2的15次方到2的15次方-1)的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1)的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1)可以看到byte和short的取值范围比较小,而long的取值范围太大,占用的空间多,基本上int可以满足我们的日常的计算了,而且int也是使用的最多的整型类型了。
1.08 public、protected、private和不写限定符有什么区别?
作用域 |
当前类 |
同一package |
子孙类 |
其他package |
public |
√ |
√ |
√ |
√ |
protected |
√ |
√ |
√ |
× |
friendly(不写时默认) |
√ |
√ |
× |
× |
private |
√ |
× |
× |
× |
1.09 线程理解
1、请说出你所知道的线程同步的方法。
wait():是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
sleep():是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级 (b)正在运行的线程因为其它原因而阻塞。 是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
2、线程实现方法有哪些?
方法一:定义一个类实现Runnable接口,重写接口中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。调用Thread对象的start()方法,启动线程,如:
方法二:定义一个类去继承Thread父类,重写父类中的run()方法。在run()方法中加入具体的任务代码或处理逻辑。调用start方法,线程t启动,隐含的调用run()方法。如:
3、什么时候使用Thread,什么时候使用Runable
采用继承Thread类方式:
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
4、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
不能,一个对象的一个synchronized方法只能由一个线程访问。
5、多线程有哪些状态?
新建、就绪(start方法调用后)、运行、睡眠(sleep方法)、等待(wait方法)、挂起、恢复、阻塞、死亡。
1.10 各种集合理解(几种常见数据结构)
1、HashMap和Hashtable的区别:
HashMap允许一个null键值而Hashtable不可以;Hashtable是线程安全的,同步的,因此要比HashMap运行慢;
2、Collection 和 Collections的区别:
Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。Collection是个java.util下的接口,它是各种集合结构的父接口。
3、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。
4、ArrayList和Vector的区别
同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的;
数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半;
5、LinkList和ArrayList的区别,如果是在集合的开头插入一个对象,使用哪种效率高些,在集合的末尾插入一个对象,使用哪种效率高些,为什么?
ArrayList随机访问的效率要比LinkList快,但是LinkList顺序访问的效率则高过ArrayList,另外LinkList在对元素进行插入和删除操作时要比ArrayList的效率高,二者的最佳选择方法是:首先选用ArrayList当发现“向元素中插入和删除操作太多时”引发性能问题时,换用LinkList,当然处理固定元素还是选用数组.
LinkedList基于链表实现,插入元素的性能会比ArrayList高.
ArrayList基于数组实现,遍历的性能高于LinkedList.
1.11 继承、重写、重载、多态
继承是子类使用父类的方法,
重写是继承后重新实现父类的方法。
重载是在一个类里一系列参数不同名字相同的方法。
多态则是父类使用子类的方法。
子类继承父类程序运行执行顺序:
父类:
public class TestStatic {
public static String name="china";
{
System.out.println("========方法体========");
}
static{
name="England";
System.out.println("========静态程序块======");
}
TestStatic(){
System.out.println("=========构造方法========");
}
public static void main(String[] args){
System.out.println("========主方法========"+name);
}
public void test(){
System.out.println("========测试方法=========");
}
}
子类:
public class TestExtendStatic extends TestStatic{
//public static String name="HUBEI";
{
System.out.println("========无名称方法体========");
}
static{
//name="SUIZHOU";
System.out.println("========子类静态程序块======");
}
TestExtendStatic(){
System.out.println("=========子类构造方法========");
}
public void test(){
System.out.println("========子类测试方法=========");
}
public static void main(String[] args){
System.out.println("========子类主方法========"+name);
TestStatic ts = new TestExtendStatic();// 上转型对象
ts.test();
}
}
输出如下:
========静态程序块====== :父类static程序块
========子类静态程序块====== :子类static程序块 【不是静态方法】
========子类主方法========England :子类主方法
========方法体======== :父类中非静态代码块
=========构造方法======== :父类构造方法
========无名称方法体======== :子类中非静态代码块
=========子类构造方法======== :子类构造方法
========子类测试方法========= :子类测试方法
执行顺序: 父类静态变量以及静态程序块 --- 子类的静态变量以及静态程序块 --- 父类非静态代码块 --- 父类中构造方法 --- 子类中非静态代码块 --- 子类中构造方法 --- 接下来才是 对象调用的方
法。
只要是用new 创建对象,分配了内存空间,不管是将引用赋给上转型对象,还是赋给子类对象,上面方法都必须执行。
即:TestStatic ts = new TestExtendStatic();// 上转型对象
TestExtendStatic ts = new TestExtendStatic();// 子类对象
上面加粗程序都会执行。
上面程序中 ts.test(); ts作为上转型对象调用的是 子类继承的父类中的方法,因为test()在子类中被重写了,所以输出的为子类中的语句。
如果将子类中 main 方法该成如下:
public static void main(String[] args){
System.out.println("========子类主方法========"+name);
TestStatic ts = new TestExtendStatic();
ts.test();
System.out.println("-------------------------");
ts = new TestExtendStatic();
ts.test();
}
输出:
========静态程序块====== 父类中静态程序块
========子类静态程序块====== 子类中静态程序块
========子类主方法========England 子类中主方法
========方法体======== 父类中非静态代码块
=========构造方法======== 父类中构造方法
========无名称方法体======== 子类中非静态程序块
=========子类构造方法======== 子类中构造方法
========子类测试方法========= 对象具体调用的方法
------------------------- 静态变量以及程序块只执行一次
========方法体======== 父类中非静态代码块
=========构造方法======== 父类中构造方法
========无名称方法体======== 子类中非静态代码块
=========子类构造方法======== 子类中构造方法
========子类测试方法=========
如果将子类主方法 中更改为:
TestStatic ts = new TestStatic ();// 运用父类构造方法创建
ts.test();
输出为:
========静态程序块====== 父类静态程序块
========子类静态程序块====== 子类静态程序块 【因为程序在子类中运行的,所以子类的静态程序块必须运行】
========方法体======== 父类非静态程序块
=========构造方法======== 父类构造方法
========测试方法========= 父类具体方法test()
如果将上述代码放到父类中,就不会加载子类 静态程序块了。
1.12关联与依赖差别,聚合与组合差别
关联:一个类受另外一个类的影响,一个Driver类里面只跟一个Car类变量,如driver(Car),强耦合关系;
依赖:一种使用关系,弱耦合,比如两个方法driver(Car),driver(Plane);即重载;
聚合:是弱拥有关系,即A对象包含B对象,但B不是A的一部分;
组合:是一种强拥有关系,主要体现部分和整体的关系;
1.13 排序
1、java类自带排序:
组成int数组,再调用Arrays.sort(int[] a)实现升序;降序可从尾部开始输出;
2、方法二:重复for循环比较两值大小存入ArrayList中;
1.14 JAVA序列化
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序 列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
1.15 面向对象的设计原则有那些?
LSP里氏替换原则:子类与父类对象间替换;
OCP开闭原则:扩展开放,更改封闭;
SRP单一职责原则:依赖不同的具体类,不要将不相关的方法放到一个具体类中,然后具体类再关联。
ISP接口隔离原则:具体类不要实现无关接口中的方法,应使用具体类实现多个接口。
DIP依赖倒置原则:针对接口编程,不是针对实现编程
CARP组合/聚合复用原则:尽量使用组合/聚合,而不是使用继承来达到复用目的
LoD迪米特法则:类间最少通信原则,采用中间类。
1.16 Java中的网络通信有那些方式,有什么区别?
分别是TCP和UDP;
TCP是一种面向连接的保证可靠传输的协议。通过TCP实现的传输,得到的是一个顺序的无差错的数据流。发送方和接收方成对的两个socket之间必须建立连接,以便在TCP的基础上进行通信, 而UDP是一种无连接的协议,每个数据都是一个独立的信息,包括完整的源地址和目的地址 ,UDP是不可靠的。
1.17一个程序编译完成后在内存中是如何存储的?
不存储在内存条上,存储在硬盘上,当需要程序运行时,程序被加载到内存条上。
1.18 IO流
在java使用流的机制进行数据的传送,从文件到内存是输入流,从内存到文件是输出流,输入流可以通过 read读取,输出流以write或print写入,对于流可以是分为高层流和低层流,低层以一个字节或字符为单位进行处理,高层流以一批数据为单位进行处理。
FileInputStream(System.in)至InputSteamReader至BufferReader
OutputSteam(System.out)至printStream
FileReader至BufferedReader
FileWriter 至 PrintWriter或bufferWriter
分类:
字节(二进制)
FileInputStream(低层输入流)
FileOutputStream(低层输出流)
PrintStream(高层流) System.out.println()
字符(一个char)
FileReader
FileWriter
在java.io包中还有许多其他的流,主要是为了提高性能和使用方便
1.19为什么重写equals()时也要重写hashCode()?两者什么关系
因为这两个函数都可以重写;完全可以hashcode相等的对象而equals确返回false;重写hashCode是为了集合类存储这些对象的时候有个比较规则;比如Map不允许重复元素,就是通过hashCode来检测的;但是hashcode的实现,一般要满足几个特征,比如:自反性,传递性什么的。