1、hibernate的二级缓存机制?
a、hibernate缓存作用是什么
hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对数据库访问的频次,从而体改应用程序的运行性能。缓存内的数据时对数据源中的数据复制,应用程序再运行时从缓存中读取数据,在特定的时刻或时间会同步缓存和物理数据源的数据。
b、hibernate数据缓存分类:一级缓存和二级缓存,其中一级缓存又称为session缓存,它是内置的,不能被卸载(意思就是这种缓存不具有可选性,是必须有的功能,不能取消session缓存)。由于session对象的声明周期通常对应一个数据库的事务或者一个应用事务,因此他的缓存是事务范围的缓存。在一级缓存中,持久化的每个实例都具有唯一的oid,hibernate二级缓存也称为sessionFactory缓存,由于sessionFactory对象的生命周期和应用个程序的整个过程对应,因此二级缓存是进程范围或者集群范围的缓存,有可能出现并发的问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别,二级缓存是可选的,是一个可配置的插件,在默认的情况下,sessionFactory是不会启用这个插件。
c、什么样的数据适合存到二级缓存中?
1、很少被修改的数据。
2、不是很重要的数据,允许出现偶尔的并发的数据
3、不会被并发访问的数据。
4、常量数据。
d、不适合放入二级缓存中的数据?
1、经常被修改的数据。
2、绝对允许出现并发访问的数据,如财务数据,绝对不允许出现并发。
3、与其他应用共享的数据。
e、hibernate查找对象如何应用缓存?
当hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查,查不到,如果配置了二级缓存,那么从二级缓存中查询,如果查不到,再去数据库中查询,如果结果按照ID放入到缓存、删除、更新、增加到数据的时候,同时更新缓存。hibernate管理缓存实例无论何时,当你给save,update或者saveOrUpdate方法传递一个对象时,或是使用load,get,list,iterate或scroll方法获取一个对象时,该对象都将被加入到session的内部不缓存中。当随后flush方法被调用时,对象的状态会和数据库取得同步。如果你不希望此同步操作发生,或者你正在处理大量对象,需要对有效管理内存时,米可以调用evict方法,从一级缓存中去掉这些对象及集合。
2、几种常见数据库取前十条数据的sql语句。
access:select top (10) * from table1 where 1=1
db2:select column from table where 1=1 fetch first 10 rows only
mysql:select * from table1 where 1=1 limit 10
sql server:
读取前10条:select top (10) * from table1 where 1=1
读取后10条:select top (10) * from table1 order by id desc
oracle:select * from table1 where rownum<=10
3、线程的基本概念,线程的基本状态以及状态之间的关系?
a、什么是线程?
一个线程是进程的一个顺序执行流,同类的多线程共享一块儿内存空间和一组系统资源,线程本身有一个供程序执行的堆栈。线程在切换时负荷小,因此,线程也被称为轻负荷进程。一个进程中可以包含多个线程。
换一种解释方法:线程是轻量级的进程,是程序执行流的最小单元,一个标准的线程由线程ID、当前指令指针(pc),寄存器集合和堆栈组成。线程不拥有系统资源,只拥有少量的运行必须的资源。
b、线程和进程的区别?
一个进程至少拥有一个线程。线程的划分尺度小于进程,使得多进程的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多线程共享内存,从而极大提高了程序的运行效率。线程在执行的过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但是操作系统并没有讲多个线程看做多个独立的应用来实现进程的调度和资源分配。
d、并发原理
多个线程或者进程“同时”运行只是我们感官上的一种表现,事实上进程和线程是并发运行的,os的线程调度机制将时间划分为很多时间片段,尽可能的均匀分配给正在运行的程序,获取CPU时间片的线程或进程得以执行,其他则等待。而CPU则在这些进程或者线程上来回切换运行。微观上所有进程和线程都是走走停停,宏观上都在运行,这种运行现象叫做并发。但是不是绝对意义上的“同时发生”。
c、线程状态
基本状态有:就绪状态,阻塞状态和运行状态三种基本状态。
就绪状态:指线程具备运行的所有条件,逻辑上可以运行,在等待处理机调用。
运行状态:指线程占有处理机正在运行。
阻塞状态:指线程在等待一个事件,逻辑上不执行。
细致划分又可分为如下几种状态:
i、新建状态:用new语句创建的线程对象处于新建状态,此时它和其他java对象一样,只是被分配了内存。
ii、等待状态:当线程new之后,并在条用start方法前,线程处于等待状态。
iii、就绪状态:当一个线程对象被创建后,其他线程调用它的start方法,该线程就进入就绪状态。处于整个状态的线程位于java虚拟机中的可运行池中,等待获取cpu的使用权。
iv、运行状态:处于这个状态的线程占用cpu,执行程序代码。在并发环境中,如果计算机只有一个cpu,name任何时刻只会有一个线程处于这个状态。【只有处于就绪状态的线程才有机会进入运行状态】
v、阻塞状态:是指线程因为某些原因放弃CPU、暂时停止运行。当线程处于阻塞状态时,java虚拟机不会给线程分配CPU,知道线程重新进入就绪状态,它才会有机会获得运行状态。
阻塞状态分为三种情况:
a、等待阻塞:运行的线程执行wait方法,jvm会把该线程放入等待池中。
b、同步阻塞:运行的线程在获取对象同步锁时,如该同步锁被其他线程占用,则jvm会把线程放入锁池中。
c、其他阻塞:运行的线程执行sleep方法,或者发出i/o请求时,JVM会把线程设为阻塞状态。当sleep状态超时、或者I/O处理完毕,线程会重新转入就绪状态。
vi、死亡状态:当线程执行完run方法中的代码,或者遇到未被捕获的异常,就会退出run方法,此时就进入死亡状态,该线程死亡。
4、多线程有几种实现方法。都是什么?
java实现多线程的方法有两种:
1、继承Thread类。
2、实现Runable接口,再new Thread(yourRunable Object)。
一个类如果实现Runable接口或者继承Thread类,那么他就是一个多线程类,如果要是实现多线程,还需要重写run方法,所以run方法是多线程的入口。
但是在启动多线程的时候,不是从run方法开始的,而是从start方法开始的,理由:当执行多线程的时候,每个线程会抢占资源,而操作系统会为其分配资源,在start方法中不仅执行了多线程的代码,除此之外还调用了start0方法,该方法的声明是native,在java语言中用一种技术叫JNI,即java native Interface,该技术的特点是使用java调用本机操作系统提供的函数,但是有一个缺点就是不能离开特定的操作系统,如果线程需要执行,必须有操作系统去分配资源,所有此操作主要是JVM根据不同的操作系统来实现的。
如果多线程是通过实现Runable接口实现的,那么与通过继承Thread实现的有一个区别,那就是多线程的启动方式----必须是通过start方法来启动,但是Runable接口只有一个方法,并没有start方法,所以在启动多线程时必须调用Thread类的一个构造方法-----Thread(Runable target),该构造方法得到Runable的一个实现。
共同点:无论哪种方法,都必须用Thread产生线程,然后调用start方法。
举例:
//一个类只要继承了Thread类,则此类就是多线程类 class MyThread extends Thread { private String name; public MyThread(String name) { this.name=name; } //如果要使用多线程,则必须有一个方法的主体 public void run() { //打印输出 for(int i=0;i<10;i++) { System.out.println(this.name+"----->运行、、、、"); } } } public class Demo1 { public static void main(String args[]) { //第一种方法 Runnable r1=new MyThread("线程A"); Runnable r2=new MyThread("线程B"); Runnable r3=new MyThread("线程C"); Thread t1=new Thread(r1); Thread t2=new Thread(r2); Thread t3=new Thread(r3); t1.start(); t2.start(); t3.start();
//第二种方法 // MyThread mt1=new MyThread("线程A"); // MyThread mt2=new MyThread("线程B"); // MyThread mt3=new MyThread("线程C"); // mt1.start(); // mt1.start();//线程只能启动一次 // mt2.start(); // mt3.start(); } }
class MyThread1 implements Runnable { private int ticket=10; public void run() { for(int i=0;i<500;i++){ if(this.ticket>0) { System.out.println("卖票----->"+(this.ticket--)); } } } } public class Demo2 { public static void main(String args[]) { MyThread1 mt=new MyThread1(); Thread t1=new Thread(mt); Thread t2=new Thread(mt); Thread t3=new Thread(mt); t1.start(); t2.start(); t3.start(); } }
5、实现多线程同步的几种方式?
概要:当多线程同步访问一个资源时,非常容易出现安全问题。因此需要采用同步机制来解决问题,java中有很多中实现成同步的方式,下面简述常用的三种:
(1)、synchronized关键字
synchronized的两种用法(synchronized方法和synchronized代码块儿)在方法的声明前加入synchronized关键字修饰。synchronized块既可以把任意代码块声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。
(2)wait方法和notify方法
当使用synchronized来修饰某个共享资源时,如果线程A1执行synchronized代码,另外一个线程A2同时执行同一个对象的同一synchronized代码时,线程A2将要等到线程A1执行完后,才能继续执行。这种情况下可以使用wait和notify方法
在synchronized代码被执行期间,线程可以调用对象的wait方法,释放对象锁,进入等待状态,并且可以调用notify方法或者notify方法通知正在等待的其他线程,notify方法只能唤醒一个线程(等待队列中的第一个线程)并允许其获得对象锁,notifyAll方法会唤醒所有等待的这个对象的线程并允许他们去竞争获取这个锁。
(3)lock
class MyThread1 implements Runnable { private int ticket=10; public void run() { for(int i=0;i<500;i++) { if(this.ticket>0) { System.out.println("卖票----->"+(this.ticket--)); } } } } public class Demo2 { public static void main(String args[]) { MyThread1 mt=new MyThread1(); Thread t1=new Thread(mt); Thread t2=new Thread(mt); Thread t3=new Thread(mt); t1.start(); t2.start(); t3.start(); } }
6、heap和stack有什么区别?
i、Stack(栈)是JVM的内存指令区,Heap(堆)是JVM的内存数据区。
Stack管理很简单,push一 定长度字节的数据或者指令,Stack指针压栈相应的字节位移;pop一定字节长度数据或者指令,Stack指针弹栈。Stack的速度很快,管理很简 单,并且每次操作的数据或者指令字节长度是已知的。所以Java 基本数据类型,Java 指令代码,常量都保存在Stack中。
Heap 的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在Heap 中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在 Stack中),在Heap 中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在Heap 中分配好以后,需要在Stack中保存一个4字节的Heap 内存地址,用来定位该对象实例在Heap 中的位置,便于找到该对象实例。
ii、stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的。Heap是运行时可动态分配的数据区,从速度看比Stack慢,Heap里面的数据不共享,大小和生存期都可以在运行时再确定。
iii、stack空间有限,heap的空间是很大的自由区,在java中,若只是声明一个对象,则先在栈内存中为其分配地址空间,若在new一下,实例化它,则在堆中为其分配地。每new一次都一定会创建新对象,因为堆数据不共享。
iv、Stack存取速度仅次于寄存器,存储效率比heap高,可共享存储数据,但是其中数据的大小和生存期必须在运行前确定。
JVM的体系结构
我们首先要搞清楚的是:什么是数据以及什么是指令。然后要搞清楚对象的方法和对象的属性分别保存在哪里。
1)方法本身是指令的操作码部分,保存在Stack中;
2)方法内部变量作为指令的操作数部分,跟在指令的操作码之后,保存在Stack中(实际上是简单类型保存在Stack中,对象类型在Stack中保存地址,在Heap 中保存值);上述的指令操作码和指令操作数构成了完整的Java 指令。
3)对象实例包括其属性值作为数据,保存在数据区Heap 中。
非静态的对象属性作为对象实例的一部分保存在Heap 中,而对象实例必须通过Stack中保存的地址指针才能访问到。因此能否访问到对象实例以及它的非静态属性值完全取决于能否获得对象实例在Stack中的地址指针。
7、方法和属性的静态和非静态区别?
静态方法和非静态方法
很大的不同之处在于,非静态方法有一个隐含的传入参数,改参数是JVM给的,和我们怎么写代码无关,这个隐含指令就是对象实例在stack中的地址指针,因此非静态方法(在Stack中的指令代码)总是可以找到自己的专用数据(在heap中的对象属性)。当然非静态方法必须获得该隐含参数,因此非静态方法在调用前,必须new一个对象实例,获得stack的地址指针,否则jvm无法将隐含参数传递给非静态方法。
静态方法则无此隐含参数,因此也不需要new对象,只要class文件被classLoader 加载到JVM的stack,该静态方法就可以被调用,当然此时静态方法是存取不到heap中的对象属性的。
总结一下过程:当一个class文件被类加载器加载到JVM中后,方法指令保存在stack中,此时heap区没有数据。然后程序指令器开始执行质量,如果是静态方法,直接执行质量代码,当然此时指令代码不能访问heap数据去;如果是非静态方法,又有隐含参数没有值,会报错。因此非静态方法执行前,需要先new对象,在Heap中分配数据,并把stack中的地址交给非静态方法,这丫个程序计数器依次执行质量,而指令代买此时能访问到heap数据区。
静态属性和非静态属性
对象实例以及动态属性是保存在heap中的,而heap必须通过stack中的地址指针才能够被指令(类的方法)访问到。因此可以推断:静态属性是保存在stack中的,而非静态属性是保存在heap中的。
正因为都是在stack中,而stack中质量和数据都是定长的,因此很容易算出偏移量,也因此不管什么指令(类的方法),都可以访问到类的静态属性,也正因为静态属性被保存在stack中,所以具有全局属性。
8、请描述你使用过的DAO层框架的有缺点?
(1)MyBatis框架:MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
优点:
1. 与JDBC相比,减少了50%以上的代码量。
2. MyBatis是最简单的持久化框架,小巧并且简单易学。
3. MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
4. 提供XML标签,支持编写动态SQL语句。
5. 提供映射标签,支持对象与数据库的ORM字段关系映射。
缺点:
1. SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
2. SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
使用场合:MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
(2)hibernate框架
优点:
1、Hibernate功能强大,是JAVA应用与关系数据库之间的桥梁,较JDBC方式操作数据库代码量大大减少,提高了持久化代码的开发速度,降低维护成本。
2、Hibernate支持许多面向对象的特性,如组合,继承,多态等,使开发人员不必再面向业务领域的对象模型和面向数据库的关系模型之间来回切换,方便开发人员进行领域驱动的面向对象的设计与开发
3、可移植性好
4、Hibernate框架开源免费,适用于大中型项目
缺点
1、不适合以数据为中心大量使用存储过程的应用
2、大规模的批量插入,修改和删除不适合用Hibernate,Hibernate不适用于小型项目,也不适用于关系模型设计不合理,不规范的系统。
9、常见的表关联有哪些类型?分别描述每种关联的效果。
一对一关系:两个表,在第一个表中的某一行只与第二个表中的一行相关,同时第二表中的某一行,也只与第一个表中的一行相关,这种关系称之为一对一关系。例如:姓名与身份id号。
一对多关系:有多张表,第一张表的行可以与第二表中的一行或者多行相关联。但是第二个表中的一行只能与第一张表中一行相关联。从第一张表的角度看时一对多,从第二张表的角度看就是多对一。例如:学生信息表和班级表就是多对一,但是班级表和学生信息表就是一对多。
多对多关系:有两张表,第一张表的一行可以与第二张表的多行相关联,同时第二张表的一行可以与第一张表的多行相关联。例如:学生信息表和教师信息表之间的关系。
多表关联查询:
(1)外连接:左外连接、右外连接、完全外连接。
左外连接:left join 或者 left outer join---select * from a left join b on a.id=b.id;
左外连接包含left join左表所有行,如果左表中某行在右表没有匹配,则结果中对应行右表的部分全部为空(NULL).注:此时我们不能说结果的行数等于左表数据的行数。当然此处查询结果的行数等于左表数据的行数,因为左右两表此时为一对一关系。
右外连接:right join 或者 right outer join--select * from a right join b on a.id=b.id;
右外连接包含right join右表所有行,如果左表中某行在右表没有匹配,则结果中对应左表的部分全部为空(NULL)。
完全外连接:full join或者 full outer join---select * from a full join b on a.id=b.id;
(2)内连接 join 或 inner join
SQL语句:select * from student inner join course on student.ID=course.ID
inner join 是比较运算符,只返回符合条件的行。
此时相当于:select * from student,course where student.ID=course.ID
(3)交叉连接 cross join
概念:没有where子句的交叉连接将产生连接所涉及的表的笛卡尔积。第一个表的行数行数乘以第二个表的行数等于笛卡尔积结果集的大小---select * from student cross join course。
如果我们在此时给这条SQL加上WHERE子句的时候比如SQL:select * from student cross join course where student.ID=course.ID,此时将返回符合条件的结果集,结果和inner join所示执行结果一样。
(4)表之间的关系为一对多,多对一和多对多的连接语句
表的一对多和多对一的查询和一对一差不多,只是查询结果不同。
多对多的表关系,如学生表和课程表。一个学生可以选择多个表,一门课程也可以被多个学生选择。当两表的关系为多对多的时候,我们就需要建立一个中间表student_course 中间表至少要有两表的主键,当然也可以有其他内容。
sql语句:select s.Name,C.Cname from student_course as sc left join student as s on s.Sno=sc.Sno left join course as c on c.Cno=sc.Cno。
10、请解释事务的四个特性!
事务:一般是指要做的或所做的事情,在计算机属于中是指访问并可能更新的数据库中各种数据项得一个程序执行单元(unit)。
i、原子性(atomicity):一个是事务是一个不可分割的工作单元。是指事务包含的所有操作要么全部成功,要么全部回滚,因此事务的操作要完全应用数据库,如果操作失败则不能对数据库有任何影响。
ii、一致性(consistency):事务必须是使数据库从一个一致性状态到另一个一致性状态。一致性和原子性是密切相关的。也就是说一个事务执行前和执行后都必须保持一致性状态。用转账的例子来说,a向b转账前两者的总和为5000,那么转账几次后,两者的总和依旧是5000,这就是事务的一致性。
iii、隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务的内部的操纵及使用的数据对并发的其他事务是隔离的,并发执行的各事务不能互相干扰。(隔离性是当多个用户并发文数据库时,比如操作数据库中同一张表时,数据库为每个用户开启事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离)
iv、持久性:也被称为永久性,指一个事务一旦提交,他对数据库的数据改变是永久性的。接下的其他操作或者故障不应该对其有任何影响。
例如:我们在使用JDBC操作数据库时,在提交事务方法后提示用户操作完成,当我们程序执行完成直到看到提示后,就可以认定事务正确提交,即使这时候数据出现问题,也必须要将我们的事务完全执行完成,否则则会造成我们看到提示完成,但是数据库因为故障而没有执行事务的重大错误。
11、简单描述Collection、List、Set、Map的概念,说明HashMap、LinkedHashMap、ConcurrentHashMap的区别?
collection(容器)是最基本的集合接口,一个容器保存一组对象元素。其中List、Set都是collection的子接口。List可以通过下标来取值,值可以重复,而Set只能通过游标来取值,其值不能重复。
List元素有先后顺序,元素有index位置,元素可以重复,继承于collection接口,实现类有:ArrayList,Vector、LinkedList。
1)LinkedList采用双向循环链表实现,插入快,查找慢。
2)ArrayList变长数组算法实现,非线程安全。一次扩展0.5倍
3)Vector变长数组算法实现,线程安全,一次增长1倍
Set集合,其元素无序,不能重复添加,继承于collection接口,实现类有HashSet(是一个只有Key值得HashMap)存入的对象必须定义hashCode();
Map散列表,也是一个接口,是以键值对方式实现集合的额,一个map不能包含相同的key值,每个key值只能映射一个value;
Map 同样对每个元素保存一份,但这是基于 " 键 " 的, Map 也有内置的排序,因而不关心元素添加的顺序。如果添加元素的顺序对你很重要,应该使用 LinkedHashSet 或者 LinkedHashMap.
执行效率是 Map 的一个大问题。看看 get() 要做哪些事,就会明白为什么在 ArrayList 中搜索“键”是相当慢的。而这正是 HashMap 提高速度的地方。 HashMap 使用了特殊的值,称为“散列码” (hash code) ,来取代对键的缓慢搜索。“散列码”是“相对唯一”用以代表对象的 int 值,它是通过将该对象的某些信息进行转换而生成的(在下面总结二:需要的注意的地方有更进一步探讨)。所有 Java 对象都能产生散列码,因为 hashCode() 是定义在基类 Object 中的方法 。 HashMap 就是使用对象的 hashCode() 进行快速查询的。此方法能够显著提高性能。
HashMap、LinkedHashMap、ConcurrentHashMap的区别
这三者都是Map接口的实现类。hashmap是基于hash算法实现,hashtable具有线程安全的特点,treemap取出来的键值对是排序后的,LinkedHashMap是按照插入顺序取出来的键值对,ConcurrentHashMap是并发包下Map,具有线程安全的特点。
HashMap是基于hash算法(hash算法具体思想请自行查询,Hash算法可以这么理解:key通过hash函数后会得到index),HashMap保存键值对的对象是Entry<K,V>。HashMap是基于Entry来保存键值对的,HashMap使用Entry数组来保存数据。而每一个Entry是以链表的形式链接一起,也就是相同根据hash值计算出来的数组位置索引(index)会出现重复,如果两个位置重复的,但是key不相等的话,会以链表的形势连接起来。
LinkedHashMap的主要特点是保持着键值对put进来的顺序。LinkedHashMap是继承的HashMap的,并且没有重写put方法。但是重写了Entry对象,在每个Entry对象里面都有两个引用:前、后(当前元素的前面的元素和后面的元素,也就是双向链表),所以存放的元素的结果跟HashMap一模一样,不同的是每一个元素有指向前面元素和后面元素的引用(理解为关系)。LinkedHashMap持有第一个键值对(也就是链表的头),这样在put元素的时候,维持好前后关系,也就相当于保持了put进来元素的顺序。
Hashtable的线性安全Hashtable实现大体和HashMap一样,Hashtable的特点是线性安全,线性安全主要是为了解决多线程操作的场景,最简单的方式就是同步(使用synchronized关键字)。
ConcurrentHashMap的线性安全
ConcurrentHashMap的实现也跟HashMap相似,不同的是ConcurrentHashMap不是 Entry 数组,而是 Segment 数组。Segment 继承 ReentrantLock,在调用 ConcurrentHashMap 的 put 方法的时候,最后调用的是 Segment 的 put 方法。而在调用 Segment 方法的时候,Segment 会先 tryLock,最后在 finally 里面调用 unlock 释放锁。
11、java中抽象类和接口的区别,什么情况下使用抽象类,什么情况系适合使用接口?
参数 | 抽象类 | 接口 |
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 |
子类使用extends关键字来继承抽象类。如果子类不是抽象类的话, 它需要提供抽象类中所有声明的方法的实现。 |
子类使用关键字implements来实现接口。 它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 |
如果你往抽象类中添加新的方法,你可以给它提供默认的实现。 因此你不需要改变你现在的代码。 |
如果你往接口中添加方法,那么你必须改变实现该接口的类 |
1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
3.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。
4.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
5.接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
6.抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
7.接口中的方法默认都是 public,abstract 类型的。
interface在某些地方和abstract有相似的地方,但是采用哪种方式来声明类主要参照以下两点:
1. 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
2. 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。
12、tomcat下有哪些目录,这些目录的作用是什么?
- /bin:存放windows或Linux平台上启动和关闭Tomcat的脚本文件
- /conf:存放Tomcat服务器的各种全局配置文件,其中最重要的是server.xml和web.xml
- /doc:存放Tomcat文档
- /server:包含三个子目录:classes、lib和webapps
- /server/lib:存放Tomcat服务器所需的各种JAR文件
- /server/webapps:存放Tomcat自带的两个WEB应用admin应用和 manager应用
- /common/lib:存放Tomcat服务器以及所有web应用都可以访问的jar文件
- /shared/lib:存放所有web应用都可以访问的jar文件(但是不能被Tomcat服务器访问)
- /logs:存放Tomcat执行时的日志文件
- /src:存放Tomcat的源代码
- /webapps:Tomcat的主要Web发布目录,默认情况下把Web应用文件放于此目录
- /work:存放JSP编译后产生的class文件