1、实现多态的三种方式
继承,重写,父类引用指向子类对象。
父类- HttpServlet
public class HttpServlet {
public void service() {
System.out.println("HttpServlet - service");
doGet();
}
public void doGet() {
System.out.print("HttpServlet - doGet");
}
}
子类- MyServlet
public class MyServlet extends HttpServlet {
public void doGet() {
System.out.println("MyServlet - do Get");
}
}
测试类 - Test
public class Test {
public static void main(String[] args) {
HttpServlet s = new MyServlet();
s.service();
}
}
2、接口和抽象类的区别
含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
下面比较一下两者的语法区别:
(1)抽象类可以有构造方法,接口中不能有构造方法。
(2)抽象类中可以有普通成员变量,接口中没有普通成员变量
(3)抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
(4) 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(5)抽象类中可以包含静态方法,接口中不能包含静态方法(jdk8之后接口可以有静态方法)
(6)抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
(7)一个类可以实现多个接口,但只能继承一个抽象类。
3、array和arraylist的区别
(1)Array类型的变量在声明的同时必须进行实例化(至少得初花数组的大小),而ArrayList可以只是先声明;
(2)Array始终是连续存放的;而ArrayList的存放不一定连续;
(3)Array对象的初始化必须指定大小,且创建后的数组大小是固定的;而ArrayList的大小可以动态指定,空间大小可以任意增加;
(4)Array不能随意添加、删除;而ArrayList可以在任意位置插入和删除
4、线程池有什么用
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
5、什么情况用线程池
(1)单个任务处理的时间比较短
(2)将需处理的任务的数量大
多线程:适合小量访问的环境中,并且要保证不会有大量的并发发生才可以勉强这样写,不会有一大群线程,因为缺少资源等等原因活不过来
线程池,就是在调用线程的时候初使化一定数量的线程,有线程过来的时候,先检测初使化的线程还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处理,就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用于了,那没得办法,另外的线程就只有等了,直到有新的“服务员”为止。线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并发而因为资源不足挂掉。
6、面向对象三个特性
封装
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。
多态
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
7、为什么要用多线程
1)更多的处理器核心
一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心加入也无法显著该程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。
(2)更快的响应时间
可以使用多线程技术,将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列)。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短响应时间,提升用户体验。
例如:用户提交一次订单,后续会等待一系列操作全部完成才能看到订购成功的结果。对于其中的一些业务操作,可以考虑交给其他线程处理,从而使其更快地完成。
(3)更好的编程模型
Java为多线程编程提供了良好、考究并且一致的编程模型,当开发人员为所遇到的问题建立合适的模型之后,稍作修改就能够方便地映射到Java提供的多线程编程模型上。
8、静态⽅法和实例⽅法有何不同
静态方bai法和实例方法的区别主要体现在两个方面:
在外部调用静zhi态方法时,可dao以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
9、静态变量和实例变量
静态变量不属于某个实例对象,而是属于类,也叫类变量,只要程序加载了类的字节码,不用创建任何实例对象就会被分配空间,就可以被使用,也就是说,你创建了多个对象,他们共用了一个静态变量,而实例对象是属于自己的独有的,不会被共享
10、六大存储区域
寄存器(register)
这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。java中,你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。 C语言可以定义寄存器变量。
栈(stack)
存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中。
位于通用RAM(random-access memory )中,但通过它的“堆栈指针”可以从处理器哪里获得支持。堆栈指针若向下移动,入栈操作,则在栈顶分配新的内存;若向上移动,出栈操作,则在栈顶释放那些内存。这是一种快速有效的分配存储方法,仅次于寄存器。创建程序时候,JAVA编译器必须知道存储在堆栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些JAVA数据存储在堆栈中——特别是对象引用,但是JAVA对象不存储其中。
{
int c=6; //at line1
c++;//at line2
}
c++;//at line3
这是Java的普通代码块。编译器先在编译时在第一行的时候就在堆栈中分配一个储存数据为6的对象,当程序运行完这个花括后,存储为6的地址便会被销毁,于是第三行就会发生编译错误。所以这也为什么堆栈中的数据不能被多个线程共享的原因。
堆(heap)
存放所有new出来的对象。
一种通用性的内存池(也存在于RAM中(random-access memory )),用于存放所有的JAVA对象。堆不同于栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要创建一个对象的时候,只需要new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代码。用堆进行存储分配比用堆栈进行存储存储需要更多的时间。
堆内存和栈内存确实是我们常常用的东西,比如 Animal a = new Animal(); 这个时候相当于在堆内存中开辟了一个空间保存了Animal的信息以及着块空间的内存地址,然后在栈内存中划了一小快空间保存了堆中的内存地址,这个时候我们就可以说引用a指向Animal()了. 可是有时候,有个静态类.Animal,里面有个静态方法speak(); 那么可以这么直接调用Animal.sepak(); 这个时候既没有new,也没有Animal a=??; 所以既没有在堆中开辟空间也没有在栈内存中开辟空间 , 可是方法确实能执行,一切程序都运行在内存里,那么证明有新的内存区,就是静态空间了.
1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢 。
静态存储区(static storage)
又叫方法区:包含的都是在整个程序中永远唯一的元素,如class,static变量。
static修饰的成员变量存放在静态存储区中,但java对象本身不存放在静态存储区。
这里的“静态”是指“在固定的位置”。静态存储里存放程序运行时一直存在的数据。你可用关键字static来标识一个对象的特定元素是静态的,但JAVA对象本身从来不会存放在静态存储区里。
值得深思的是,static能修饰成员变量,而不能修饰方法中的临时变量,所以static final修饰的是一个成员变量,这个变量放在常量存储区中。而仅用static修饰成员变量时放在静态存储区中。
常量存储区(constant storage)
常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会被改变。有时,在嵌入式系统中,常量本身会和其他部分分割离开,所以在这种情况下,可以选择将其放在ROM(read only memory)中。
声明为final static的为常量,可以保存在常量储存区,还有String类型的对象都是常量,系统维护了一个String常量池。 String类型是final修饰的,无法被继承。
String s = new String("Hello world!");
上面这行代码一共创建了几个对象?答案是不确定的,因为不知道常量池中存不存在Hello world!字符常量。如果存在,则只创建一个String类型的对象在堆上,如果不存在则在堆上创建一个String对象,并在常量池创建一个字符常量Hello world!对象。需要注意的是,常量池在java类加载的过程中会有很多字符常量被创建,需要比对的字符常量可能已经存在,只是你不知道而已。
非RAM存储区
若数据完全活在程序外部,不依赖于程序时,它可以不受程序的任何控制,即使程序未运行依然可以存在,比如流对象和持久化对象。对于流对象来说,对象转化为字节流,发送给另一台机器;对于持久化对象来说,通常存放于磁盘上,即使程序终止,仍可以保持自己的状态。此存储方式的技巧在于:把对象转化为可以存放在任何其它媒介的事物上,在需要时,恢复为常规的、基于RAM的对象。
非RAM存储器,主要就是磁带,磁盘等等。
11、String、StringBuffer、StringBuilder
String
String:字符串常量,字符串长度不可变。Java 中 String 是 immutable(不可变)的。
用于存放字符的数组被声明为 final 的,因此只能赋值一次,不可再更改。
StringBuffer(JDK1.0)
StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。
Java.lang.StringBuffer 线程安全的可变字符序列。在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。
append 方法始终将这些字符添加到缓冲区的末端;
insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是 start 的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含 startle ,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含 starlet 。
StringBuilder(JDK5.0)
StringBuilder:字符串变量(非线程安全)。在内部,StringBuilder 对象被当作是一个包含字符序列的变长数组。
java.lang.StringBuilder 是一个可变的字符序列,是 JDK5.0 新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。
其构造方法如下:
构造方法描述StringBuilder()创建一个容量为16的StringBuilder对象(16个空元素)StringBuilder(CharSequence cs)创建一个包含cs的StringBuilder对象,末尾附加16个空元素StringBuilder(int initCapacity)创建一个容量为initCapacity的StringBuilder对象StringBuilder(String s)创建一个包含s的StringBuilder对象,末尾附加16个空元素
在大部分情况下,StringBuilder > StringBuffer。这主要是由于前者不需要考虑线程安全。
三者区别
String 类型和 StringBuffer 的主要性能区别:String 是不可变的对象, 因此在每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,性能就会降低。
使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。所以多数情况下推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。
使用策略(1)基本原则:如果要操作少量的数据,用String ;单线程操作大量数据,用StringBuilder ;多线程操作大量数据,用StringBuffer。
(2)不要使用String类的"+"来进行频繁的拼接,因为那样的性能极差的,应该使用StringBuffer或StringBuilder类,这在Java的优化上是一条比较重要的原则.
**String 的 concatenation(即+)操作利用了 StringBuilder(或StringBuffer)的append 方法实现