作者:极御云安全 | 来源:互联网 | 2023-06-08 10:10
我所听说过的技术名词总结:
1.熟悉的:Java、SVN、Maven、Agile、Scrum、Spring、
2.正在了解的:Jetty、MongoDB、redis、Lucene、
3.不熟悉的:Autonomy、Quova、
4.工具:Eclipse、MyEclipse、Navicat、Plsql、springsourcetoolsuite、
需要巩固的知识:Spring、SpringMVC、Mybatis;Java、Mysql、Oracle、HTML、CSS、JS
需要了解的东西:HTTP协议、单元测试技术、J2EE体系结构、高并发大数据量的项目了解
第一部分:Mysql和Oracle面试问题总结
varchar与char的区别,char是一种固定长度的类型,varchar则是一种可变长度的类型。
varchar(50)中50的涵义,最多存放50个字节。
事务是如何通过日志来实现的。隔离性: 通过 锁 实现。原子性、一致性和持久性是通过 redo和undo来完成的。
// 你如何确定 MySQL 是否处于运行状态?
命令:service mysql status
//如何开启或停止 MySQL 服务?
命令:service mysqld start || service mysqld stop
//如何通过 Shell 登入 MySQL?
命令:mysql -u root -p
//如何列出所有数据库?
命令:show databases;
//如何切换到某个数据库并在上面工作?
命令:use database_name;
//如何列出某个数据库内所有表?
命令:show tables;
//删除数据库、删除表
命令:drop database database_name; drop table table_name;
//创建数据库
命令:creat database database_name;
//创建新表
命令:creat table_name(col1_name varchar [not null] [primary key], col2_name varchar [not null]...);
//增加一个列
命令:Alter table table_name add column col_name type;
//添加主键
命令:Alter table table_name add primary key(col);
//选择语句
select * from table_name where...
select sum(uid) from table_name where...
什么是存储过程?用什么来调用?
答:存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。可以用一个命令对象来调用存储过程。
索引的作用?和它的优点缺点是什么?
答:索引就一种特殊的查询表,数据库的搜索引擎可以利用它加速对数据的检索。它很类似与现实生活中书的目录,不需要查询整本书内容就可以找到想要的数据。索引可以是唯一的,创建索引允许指定单个列或者是多个列。缺点是它减慢了数据录入的速度,同时也增加了数据库的尺寸大小。
什么是内存泄漏?
答:一般我们所说的内存泄漏指的是堆内存的泄漏。堆内存是程序从堆中为其分配的,大小任意的,使用完后要显示释放内存。当应用程序用关键字new等创建对象时,就从堆中为它分配一块内存,使用完后程序调用free或者delete释放该内存,否则就说该内存就不能被使用,我们就说该内存被泄漏了。
第二部分:J2EE体系结构图
第三部分,单元测试技术
Junit单元测试,一个bug被隐藏的时间越长,修复这个bug的代价就越大。Junit是一个回归测试框架(regression testing framework),供Java开发人员编写单元测试。Junit测试是程序员测试,即所谓白盒测试。
黑盒测试:是通过使用整个软件或某种软件功能来严格地测试, 而并没有通过检查程序的源代码或者很清楚地了解该软件的源代码程序具体是怎样设计的。测试人员通过输入他们的数据然后看输出的结果从而了解软件怎样工作。在测试时,把程序看作一个不能打开的黑盆子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收和正确的输出。
白盒测试:是通过程序的源代码进行测试而不使用用户界面。这种类型的测试需要从代码句法发现内部代码在算法,溢出,路径,条件等等中的缺点或者错误,进而加以修正。
接下来我们讲讲Junit的使用:
正常的规范的代码开发步骤:每天早上,先检索SVN,然后单元测试,如果没问题了。然后做自己的开发任务,做完之后,执行单元测试,如果没有问题,则提交服务器。测试中用到了Java中的一个关键字assert断言。
// 先下载junit.jar包放到eclipse的项目中
// 比如新建了一个类,如下:
package com.sunrun;
public class Calculate{
public int add(int a , int b){
return a+b;
}
public int min(int a, int b){
return a-b;
}
}
//定义测试类的时候,常有如下规范:package包名与被测试类结构一致,这样的好处是不需要导入类的包,而且被编译后的.class文件是放在一起的。类名常常是TestXxx
package com.sunrun; //不需要导入类哦!!!
import org.junit.Test;
import org.junit.Before;
import org.junit.Assert; //Junit4才会有
public class TestCalculate{
Calculate cal;
@Before //执行任意一个方法之前,都会执行这段代码
public void setUp(){
}
@Test
public int testAdd(){
int rel = cal.add(2, 7);
Assert.assertEquals("加法有问题", rel, 9); //(出错提示信息, 预期值, 实际值)
}
}
//Junit3和Junit4的差别很大
1.在Junit3中,测试类必须继承TestCase; 测试方法必须以testXxx开头,如果希望在执行某个方法执行,都必须执行某个初始化方法,则该初始化方法必须命名为setUp;如果希望在某个方法运行之后,都必须运行某个方法(如释放资源),则必须使用tear down.
2.在Junit4中,一个POJO类就是一个测试类,测试方法军用@Test来标识。初始化方法用@Before来标石,释放资源的方法用@After来标识。
3.Junit4总提供了一个类叫做Assert,它提供了大量的方法进行断言处理。Junit3中继承了TestCase类,类中提供了大量的assert方法。
总结:但是为了Junit4中的测试类在Junit3中也可以使用,习惯于把初始化方法命名为setUp,释放资源的方法命名为tearDown;测试方法也同样以test开头(保持兼容)
根据上面讲的,我们引出了两个问题:第一,单元测试其中某一个方法的失败,并不会影响其他方法的测试(最致命的优点).第二,如果测试除法,是否能考虑到所有的测试用例。我想这就是从单元测试像自动化测试的转变。
//插播一下异常测试和性能测试
//如果抛出异常,则测试通过;不抛异常则测试不通过。适用于用户名和密码校验的应用场景
@Test(expected=ArithmetricException.class)
public void testDivided(int a, int b){
int real = cal.divided(20, 0);
}
//300毫秒执行完毕,才算正常。用于简单的性能测试。
@Test(timeout=300)
public void testTime(){
try{
Thread.sleep(500);
} catch(Exception e){
e.printStackTrace();
}
}
回顾一下Spring的事务(原子、一致、隔离、持久)
小回顾Spring事务管理的API,它提供了三个接口:
platformTransactionManager(平台事务管理器)
TransactionDefinition(事务定义信息:隔离、传播、超时、只读)
TransactionStatus(事务具体运行状态)
/*
*platformTransactionManager(平台事务管理器)
*TransactionDefinition(事务定义信息:隔离、传播、超时、只读)
*TransactionStatus(事务具体运行状态)
*/
//事务管理器
platformTransactionManager是一个接口,它有好多个实现类,我们要掌握的就两个,一个是支持Hibernate的,另外一个是支持JDBC和Mybatis的。
Hibernate: HibernateTransactionManager (org.springframework.orm.hibernate2.HibernateTransactionManager)
JDBC/Mybatis: DataSourceTransactionManager (org.springframework.jdbc.datasource.DataSourceTransactionManager)
//事务定义信息
TransactionDefinition,它提供了事务的隔离级别和传播特性。
模式篇--代理模式:(基本概念及其应用场景、几种实现方式)
代理模式定义,为其他对象提供代理,以控制对这个对象的访问。代理对象起到中介的作用,可以新增额外服务,取消部分功能服务。(如:火车站代售处是火车站的代理;相当于一个中介的作用,它提供了电话订票服务(这是火车站没有的)同时取消了退票服务(火车站有的))。
常见的集中代理模式:远程代理,为不同地理的对象提供局域网的代表对象。(类似于客户端和服务器)。虚拟代理,根据需要将资源消耗很大的对象进行延迟,真正需要的时候再去创建。(类似于新闻页中的图片,新闻页的图片是真是图片的一个缩影,真正需要专门去看图片的时候才会去加载真正图片,你看一下两个图片大小就知道了。)保护代理,用于提供权限控制。
/*
* 代理的两种实现方式:
* 1.静态代理
* 2.动态代理
*/
静态代理:代理和被代理对象在代理之前是确定的。它们都实现相同的接口或者继承相同的抽象类。(继承方式和聚合方式)
动态打理:
1 // 首先定义接口
2 public interface Moveable {
3 void run();
4 }
5
6 //定义了被代理类
7 public class Car implements MOveable {
8
9 @Overide
10 public void run() {
11 try{
12 Thread.sleep(new Random().nextInt(1000));
13 }catch (InterruptException e){
14 e.printStackTrace();
15 }
16 }
17
18 }
19
20 //在很久很久以前,我们如果要记录汽车的行驶时间
21 public class Car implements MOveable {
22 long startTime = System.currentTimeMills();
23 @Override
24 public void run() {
25 try{
26 Thread.sleep(new Random().nextInt(1000));
27 }catch (InterruptException e){
28 e.printStackTrace();
29 }
30 }
31 long endTime= System.currentTimeMills();
32 System.out.println("汽车行驶时间:"+(endTime - startTime) +"毫秒!");
33 }
34
35 //接下来演示一下使用继承方式和聚合方式来实现静态代理:
36
37 //?Demo1,使用继承方式实现静态代理
38 public class Car2 extends Car {
39 @Override
40 public void move(){
41 long startTime = System.currentTimeMills();
42 super.move();
43 long endTime= System.currentTimeMills();
44 System.out.println("汽车行驶时间:"+(endTime - startTime) +"毫秒!");
45 }
46 }
47
48 //?Demo2,使用聚合方式实现静态代理(聚合:一个类中调用另一个对象)
49 public class Car3 implements Moveable {
50 private Car car;
51 public Car3(Car car){
52 super();
53 this.car = car;
54 }
55 @Override
56 public void move(){
57 long startTime = System.currentTimeMills();
58 car.move();
59 long endTime= System.currentTimeMills();
60 System.out.println("汽车行驶时间:"+(endTime - startTime) +"毫秒!");
61 }
62 }
聚合方式比继承方式更适合代理模式
分析如下:比如我现在有如下功能的叠加:时间处理;增加权限管理;增加日志处理。
/*
* 我们先来看如果使用继承方式
*/
// 需求1:先记录日志,再记录汽车行驶的时间
/*
* 日志记录开始
* 汽车行驶开始
* 汽车行驶中
* 汽车行驶结束
* 日志记录结束
*/
// 需求2:先记录汽车行驶的时间,再记录日志
/*
* 汽车行驶开始
* 日志记录开始
* 汽车行驶中
* 日志记录结束
* 汽车行驶结束
*/
像上面这样,随着应用场景的叠加,代理类膨胀的函数达到n的阶乘(N!)
/*
* 我们再来看如果使用聚合方式
* 聚合方式,代理类和被代理类实现相同的接口
* 使用聚合方式,会非常灵活
*/
// 公共接口
public interface Moveable {
void run(){
}
}
// 被代理类
public class Car implements Moveable{
@Override
public void run () {
try{
Thread.sleep();
} catch (InterruptException e){
e.printStackTrace();
}
}
}
//代理类一,实现时间处理
public class CarTimeProxy implements Moveable {
private Moveable m;
public CarTimeProxy (Moveable m){
super();
this.m = m;
}
@Override
public void run(Moveable m) {
long startTime = System.currentTimeMills();
m.move();
long endTime = System.currentTimeMills();
System.out.println("运行时间:" + (endTime - startTime) + "毫秒!");
}
}
//代理类二,实现日志的代理 (注意这里实现代理类的叠加)
public class CarLogProxy implements Moveable {
private Moveable m;
public CarLogProxy (Moveable m){
super();
this.m = m;
}
@Override
public void run(Moveable m) {
System.out.println("日志开始");
m.move();
System.out.println("日志结束");
}
}
//接下来,我们使用测试类看看聚合代理的灵活性
public class TestCar extends TestCase {
public static void main(String args[]){
Car car = new Car();
/*
* 功能叠加Demo 1
* 先时间处理,后日志叠加
*/
CarTimeProxy ctp = new CarTimeProxy(car);
CarLogProxy clp = new CarLogProxy(ctp);
clp.move();
/*
* 功能叠加Demo 2
* 先记录日志,后时间处理功能叠加
*/
CarLogProxy clp = new CarLogProxy(car);
CarTimeProxy ctp = new CarTimeProxy(clp);
ctp.move();
}
}
总结一下上面的问题,解决了静态代理功能扩展的灵活性。那么如果我们写火车、飞机、坦克的相应继承了Moveable的类,能不能复用上面的那些代理类呢?就是说一个TimeProxy同时实现了对火车、汽车、自行车的代理呢?接下来就引申出来了动态代理。我们来分析一下:
1 /*
2 * 动态代理可是一个高逼格的东西,包括JDK和Spring框架都是以它为基础构建的,我们先来分析一下JDK中的动态代理。
3 * 动态的产生代理,实现对不同类,不同方法的代理。
4 */
如上图所示,ProxySubject是代理类,RealSubject是被代理类;ProxyHandler是实现了InvocationHandler接口的一个类(JDK提供),Proxy是动态产生的代理类。奉上代码:
// Demo展示
import java.lang.reflect.InvocationHandler;
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target){
super();//调用父类构造方法
this.target = target;
}
/*
* 参数说明
* Proxy 被代理对象
* Method 被代理对象的方法
* args[] 方法的参数
*
* 返回值
* Object 方法的返回值
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
long startTime = System.currentTimeMills();
method.invoke(target);
long endTime = System.currentTimeMills();
System.out.println("运行时间:" + (startTime - endTime));
return null;
}
}
// 来一个测试用例,生成动态代理类
public class Test {
Car car = new Car(); //这部分省略了,当然Car是继承了Moveable接口的
InvocationHandler h = new TimeHandler();
Class> cls = car.getClass();
/*
* 参数说明(loader, interface, innovationHandler)
* loader 类加载器
* interface 实现接口
* innovationHandler
**/
Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterface(), h);
m.move();
}
总结,所谓动态代理是这样一种类,它在运行的时候产生了一个class对象。该class需要实现一组interface,使用动态代理时,必须实现InvocationHandler接口。
说一下Lucene的搜索引擎机制:
//如搜索“数据结构 清华大学”
(content like '%数据结构%') or (content like '%清华大学%')
使用上面的like的方法,检索起来超级慢的,把用户都吓跑了
Java基础回顾
1 1.Java为什么要单继承?既然单继承为什么又要实现多个接口?
2 一个类只能继承一个其他的类:
3 在Java语言中禁止多重继承:一个类可以具有多个直接父类。多重继承不合法的原因是容易引发意义不明确。例如,有一个类C,如果允许它同时继承A类与B类(class C extends A,B{}),假如A、B两个类都有同一种方法fun(),如果定义:
4 C c = new C();
5 那么c.fun()应该调用哪一个父类的fun()方法?无法给出答案,因此Java语言禁止多重继承。 但C++却是可以的,所以C++比起java在语法上却难了些。
6
7 但一个类可以实现多个接口,这又是为什么?
8 这一特性和上一特性结合使用,可以获得和多重继承相似的效果。
9 现假如类C实现了A与B两个接口(class c implements A,c{}),且这两个接口都有一个抽象fun()方法,现在调用方法时没有任务不明确的地方,因为接口中的所有方法都是抽象,并且超类的任何方法都需在子类中地覆盖实现,所以调用时其实是调用自己本身的实现方法,没有什么调用不明确的说法。
10 2.Static、final迅速做出区分?
11 三个维度来看:基本类型变量,引用类型变量,方法、类。
12 final,修饰基本类型变量,变量不可变;修饰引用类型变量,内容可以变,不能再指向其他内容;修饰方法,指方法不可被继承;修饰类,类不能被继承。
13 static,修饰基本类型变量,就是全局变量;修饰引用类型变量,也是全局的;修饰方法;初始化一次;修饰类,一般内部类才申明为静态的,静态内部类。静态的不能调用非静态的。
View Code