手记

Java程序员面试基础知识 I

数据结构
  1. 在构建一个ArrayList时,可以选择指定底层数组的初始大小。如果尝试添加一个元素且底层数组满了,ArrayList类首先会自动重新分配一个更大的数组,将原列表内容复制进去,然后再添加新的元素。因此,如果在构建ArrayList时传入一个较大的数值,可以避免在列表增长时发生太多耗时的数组重新分配的操作。
  2. 二叉堆(binary heap)是一种平衡二叉树,维持子节点“大于”其父节点的性质。
  3. 在堆中插入元素时,首先将元素添加到树的最底层中第一个可行的位置,然后将插入的节点和其父节点进行比较,如果新插入的值更小,则交换这两个节点的值。这个过程递归地进行,新插入的值不断上浮,直到满足堆的性质而不再需要交换任何元素为止。
  4. 当堆中一个元素被删除时,用堆中最后一个元素替换这个被删除元素的位置,然后一直和某个子节点进行比较交换,直到发现两个子节点都比自己大,或者已经到了堆的底部。
  5. 映射(map)也称作散列(hash)、关联数组(associative array)或字典,是一种键-值(key-value)存储数据结构。映射数据结构有一个性质:一个键在映射中只能出现一次,如果插入同一个键,那么这个键原来关联的值就会被覆盖。
  6. 向HashMap表插入一个值时,会调用hashCode方法,这个方法返回一个表中合法范围的索引值。表的大小决定了可能冲突的概率。一种可能的冲突解决方法是采用二次散列函数。当要插入一个值时,如果在计算得到的位置上已经存在一个条目了,那么通过这个二次散列函数得到一个二次散列值,将这个值用作偏移量。这种方法只能延缓冲突的发生,Java对象只有一个hashCode方法,因此应该考虑另一种方法。另一种方法是在每一个表项中存储一个条目的列表,所有散列得到同一个表索引的表键的条目都会添加到这个索引值对应的列表中。在自毁长城键检索时,只需要遍历这个列表即可,检查每一个元素是否相等,直到找到匹配。
  7. 在构建新的HashMap对象时,可以选择指定一个负载因子:一个0和1之间表示百分比的值。当底层的表已经填满了这个百分比表示的容量时,表的大小会翻倍。表调整大小时,表中的所有元素都需要重新分配,因为hashCode值会被缩放到表中的一个索引值。最好在构造映射表时设置一个初始大小值,以避免发生表大小的重新调整。
  8. TreeMap是Map接口的另一个实现,使用了Comparable和Comparator接口。如果键是自然排序的,那么键必须遵循Comparable接口;如果是自定义排序,那么必须遵循Comparator接口。

设计模式
  1. 生成器模式(Builder Pattern)
    如果要创建一个带有很多字段的对象,那么通过构造函数创建会显得很笨拙而且混乱不清。通过生成器模式可以在一定程度上解决这个问题。使用构造器模式时,创建一个伴随对象(companion object),这个对象称为构造器(builder)。构造器的作用是构建合法域内的对象。
  2. 策略模式(Strategy Pattern)
    通过策略模式可以轻松地替换某个算法的具体实现细节,而不需要完全重写代码,可以将使用何种实现的决策推迟到运行之前,甚至可以在运行时替换实现。通常和依赖注入(dependency injection)联合使用,Spring框架通过一个XML文件来构造对象及对象之间的依赖关系。
  3. 模板模式(Template Pattern)
    模板模式的作用是将一个算法的部分步骤或所有步骤推出或委托到一个子类,公共的行为可以在超类中定义,具体的不同实现可以在子类中定义。
  4. 装饰者模式(Decorator Pattern)
    通过装饰者模式可以修改或配置特定对象的功能,例如向滚动条对象添加按钮或其他功能,再例如如何在同样一组配料的情况下针对两个不同的顾客建立三明治订单的模型。Java中用于读写JVM之外的IO源的IO类广泛使用了装饰者模式。
  5. 享元模式(Flyweight Pattern)
    享元模式适用于多个对象,而且其中很多对象表示的都是同一个值的情形:如果这些对象都是不可变的(immutable),那么就可以共享这些值。例如Integer.valueOf方法。
  6. 空对象模式(Null Object Pattern)
    空对象模式是享元模式的另一个实现,通过享元对象表示null。
  7. 单例模式(Singleton Pattern)
    单实例类指的是只允许创建一个实例的类,通常用于需要向第三方提供单一入口点的场合,例如数据库和Web服务,通过这种方式可以方便地在一个地方管理和配置多个连接。

Java 基础
  1. short、int和long的二进制值在内存中存储时采用二进制补码(Two’s Complement)。二进制补码的好处之一是只有一个零,即没有“负零”的概念,因而系统可以多存储一个负值。
  2. BigInteger可以认为是没有上界的。
  3. Java对象: 对状态和行为的封装,对象可以定义为一组变量的集合和一组方法的集合,这组变量可以看成是整合在一起表示一个复杂的实体,而这组方法提供了和这个实体相关的操作。对象都是引用类型,当创建的对象被赋给一个变量时,表示这个变量指向了那个内存位置。如果多个变量被赋予同一个对象(称为实例),那么这些变量指向的是同一个内存区域,通过一个变量对这个实例进行任何修改都会被其他变量看到。
  4. final关键字的作用对于对象和对于基本类型是一样的。变量定义时设置变量的值,然后变量表示的内存位置存储的值不能变化。基本类型和对象的变量定义和内存位置都有很大的不同。尽管对象引用不能变化,但是这个对象中的值可以变化,除非这些值本身都是final的。
  5. 对象中的可见性修饰符(visibility modifier)控制的是对类中封装的状态以及控制实例行为的方法的访问权。4种类型的修饰符定义如下:

  6. static修饰符:类中定义的静态方法和变量属于类,但是不属于某个实例。静态方法和变量是所有实例共享的,通常通过类名来访问,而不是通过某个具体的实例来访问。
  7. 多态(polymorphism)和继承(inheritance)是面向对象开发中的两个核心概念。通过多态,可以为某一特定类型的行为进行定义,并且可以针对这种行为提供多种不同的实现类。通过继承,可以在定义一个类时从父类继承行为。
  8. 运行在JVM上的每一个类都是从java.lang.Object继承而来的。
  9. equals(Object other) 方法的用途是测试两个引用表示的对象是否逻辑上相等。对于集合类,例如java.util.TreeMap和java.util.HashMap等,利用对象的equals方法判定对象是否已经在集合中存在。Object实现的equals方法比较的是对象在内存中的位置。
  10. hashCode的规则是:对于两个相等的对象,必须返回同一个值。然而,如果对象返回的是同一个hashCode,不一定意味着两个对象相等。要注意的是,hashCode返回的是一个int,这意味着如果要求不同的hashCode表示不相等的对象,那么某一个类型的实例最多只能有232种不同的值。这是一个很大的限制,特别是对于String这样的对象。和equals一样,Object类的hashCode方法也是通过内存位置生成hashCode值。这意味着两个在不同位置但逻辑上相等的实例返回的hashCode会不同。
  11. 如果要重写hashCode或equals方法,那么这两个方法都必须重写。
  12. hashCode和equals之间有这样的关系的原因和java.util.HashMap这一类集合类的实现方式有关。支撑HashMap的数据结构是某种类型的表,例如数组或列表,hashCode值的作用是判定表中保存对象要使用的索引值。由于hashCode返回的值是int,所以其值可能为负,也可能比表的大小要大。任何HashMap的实现都会对这个值进行操作,从而得到合法的表索引值。如果不相等的对象具有同样的hashCode值,那么这些对象都会通过LinkedList数据结构保存在同一个索引。
  13. Java中的数组也是对象。因此数组是通过引用传递的。
  14. String表示的值使用char数组来保存。String的值永远不会变化,String是不可变的。String的substring、replace和split方法返回的都是新的字符串拷贝,新字符串的内容做了相应的修改。所有的数值类,例如Integer、Double、Character和BigInteger也都是不可变的。
  15. String驻留(interning):当JVM加载类时,会将所有的String字面量保存在一个常量池中,如果出现了重复的String字面量,那么重复字面量可以通过池中已经存在的相同常量来引用。任何String实例都可以通过intern()方法添加到这个驻留池中。如果要对来自一个文件或网络连接的大量数据进行解析,而且这些数据可能包含大量的重复内容,那么可以使用String驻留技术。但是如果对intern()方法的滥用导致常量池中存在很多条目,例如数百万条目,那么每一个条目的查询开销也会影响应用程序的运行性能。
  16. String常量池是享元模式的一种实现。
  17. 结合泛型(generic)使用Collections API时,编译器就知道约束集合只允许包含特定类型的对象。
  18. 在应用泛型时存在类型变体(type variance),可通过通配符(wildcard)告诉编译器这里允许A的扩展类的任何实例,如:List<? extends A>。尽管B是A的子类,但是List<B>并不是List<A>的子类。
  19. 具体化(reified):本质上说,具体化的意思就是在运行时生效。Java的泛型类型是没有具体化的,即编译器检查实现代码使用泛型参数是否正确时使用的所有类型信息都不是.class文件中定义的类型信息。
  20. 自动装箱和装箱的区别:Java 5引入了自动装箱的概念,指的是自动地将基本类型转换为对应的引用类型,例如boolean转换为Boolean,以及int转换为Integer。在Java 5之前,这种操作必须手工完成,即装箱操作,为也将int转换为Integer,必须构造一个(new Integer(42)),或使用工厂模式(Integer.valueOf(42))。
  21. 拆箱: 将装箱的引用类型,例如Float、Integer和Boolean转换为对应的基本类型float、int和boolean的过程。必须注意引用为null的情况。当编译器将Integer转换为int时,会假定被转换的值不为null,如果被转换的值为null,则会立即抛出NullPointerException异常,因为无法将null赋值给基本类型。
  22. 基本类型不能用于泛型类型定义,即没有List<int>等类型,泛型中必须使用引用类型。
  23. 注记(annotation):在Java 5中引入的,Junit库从Junit 4开始充分利用这些注记。方法可以有多个注记。
  24. @Override注记:这条指令告诉编译器,有一个父类的方法要被重写。如果在父类中没有匹配的方法签名,那么表示出现了一个错误,编译应该停止。这是避免在重写方法时产生错误的绝好方法。
  25. Java异常层次结构:

    如果一个方法(或构造函数)可能会抛出检查异常,那么在方法定义中应该显示地定义这个异常。这个方法的所有调用者都必须准备好处理这个异常,处理的方法既可以是将异常抛出给调用自己的方法,也可以是将方法调用包装在一个try/catch/finally代码块中,并且根据具体情况处理异常。Java标准库中的所有异常都可以在构造函数中接受一个throwable实例,如果要创建新的Exception类,请一定遵循这个规则。
  26. 运行时异常(runtime exception)和检查异常(checked exception):异常本身的定义分为两类:一个异常既可指运行时异常,也可指检查异常。运行时异常由RuntimeException的子类表示,检查异常则是任何其他的异常。作为一般性原则,RuntimeException异常是任何一位细心的开发者都应该避免的异常,如ArrayIndexOutOfBoundsException、NullPointerException。
  27. 异常类(exception chaining):当抛出一个之前已经捕捉到的异常里,建议抛出一个新的异常,并且在这个新的异常中添加一个引用,这种技术称为异常链。需要重新抛出异常的原因包括将一个检查异常转换为一个运行时异常,以及对异常执行一些日志操作然后再重新抛出这个异常。在对没有处理的异常进行调试时,栈跟踪记录可以在应用程序控制台输出所有这些信息,通过“caused by” 行可以找到被包装前或重新抛出前的原始异常。
  28. try-with-resources语句:Java 7为try/catch/finally语句引入了一种语法糖,如果一个类实现了AutoCloseable接口,那么不需要担心资源关闭的问题。AutoCloseable接口指定了一个方法close(),这个方法会在try代码块之后调用,就好像在代码块的finally部分调用一样。
  29. 通过反射API(Reflection API)可以访问并修改所有字段,不论这些字段的可见性如何。通过反射机制对TestClass类的私有字段name进行修改:

    public final class TestClass {
    private String name;
    public TestClass(String s){
        name = s;
    }
    public String getName(){
        return name;
    }
    public static void main(String[] args){
        final TestClass testClass = new TestClass(“initName”); 
        // 使用反射API访问私有字段name
        final Field name = testClass.getClass().getDeclaredField(“name”);
        name.setAccessible(true);
        name.set(testClass, “changedName”);
        System.out.println(testClass.getName());    // 将会输出changedName
    }
    }
  30. 集合API类: 大致来说,集合框架包含三类:Set (HashSet、TreeSet、LinkedHashSet)、List (ArrayList、LinkedList、Vector)以及Map (HashMap、Hashtable、TreeMap)。还有一个专门的Queue接口 (PriorityQueue)。Java Collection框架包含在java.util包中,所有的单元素集合都实现了Collection接口,映射的实现没有实现Collection接口。Java倾向于将映射和集合分开,但二者还是有关联的:Map接口包含的entrySet()、keySet()和values()方法都以Collection的形式返回Map中的不同数据。

  31. LinkedHashMap具有HashMap的所有特性(根据key索引快速查找元素)但同时还能保留进入映射数据结构的条目的顺序。
  32. Hashtable和HashMap:Hashtable类是synchronized,尽管对并行任务很有效,但对单线程任务会有显著的性能开销。而HashMap类不是synchronized,因此开发者可以自行将这个类的使用调整为满足任何特定的并发需求。建议在并行环境中需要严肃使用Map接口的地方使用ConcurrentHashMap类(Java 5中引入)。
  33. Java虚拟机(Java Virtual Machine, JVM)是Java程序运行的平台。 Java程序被编译为字节码(bytecode)的形式(通过javac),JVM将字节码解释为适合底层架构和操作系统运行的具体指令。
  34. 内存的分配:内存主要被分为栈和堆。new关键字在Java堆(heap)中分配内存。堆是主要的内存池,整个应用程序都可以访问。如果无法为某个对象分配内存,则JVM会尝试通过垃圾回收机制回收一些内存。如果仍然无法获得足够的内存,则JVM抛出OutOfMemoryException异常并退出。
  35. 代(generation):堆被分为一些不同的区域,这些区域称为代(generation)。随着对象在越来越多次的垃圾回收中生存下来,它们会被提升至不同的代中。越老的代的垃圾回收频率越低。对象刚创建时被分配在Eden Space中,如果在一次垃圾回收中存活下来了,那么这些对象会被晋升到Survivor Space中。如果对象在Survivor Space中生存了更长的时间,那么这些对象会被分配到Tenured Generation中,这一代垃圾回收的频率要低得多。还有一个第4代,称为Permanent Generation,或简称为PermGen,这一代中的对象通常包含JVM运行时必不可少的不可变状态数据,例如类的定义和String常量池,这些对象是不会被垃圾回收的。
  36. 垃圾回收(garbage collection)指的是一种重新回收之前分配过的内存的机制,回收的内存可以重新给未来的内存分配使用。在Java中,当新构造一个对象时(通常是通过new关键字构造),JVM会为对象及其要保存的数据分配足够多的内存。当这个对象不再需要时,JVM需要重新回收这块内存。Java传统使用的垃圾回收算法是标记-清理算法。运行代码中的每一个对象引用都被标记为活跃,这些对象中的每一个引用都会被遍历,并且也被标记为活跃,直到从活跃对象出发的所有路径都被跟踪了。这个过程完成之后,堆中的每一个对象都会被扫描,那些没有被标记为活跃的内存位置都会被设置为可回收。在这个过程中,为了能够成功地回收内存,JVM中的所有线程都会被暂停,这种方式称为“停止一切(stop-the-world)”的回收。
  37. 栈中保存的内存包括原始值、指向对象的引用以及方法。栈上变量的生命期是由代码作用域决定的。一旦执行流程离开了作用域,那么这个作用域中声明的变量就都会从栈中移出。当调用一个方法时,这些声明的变量会被放置在栈的顶部,在一个栈中调用另一个方法时,新方法的变量会被压入栈中。
  38. Java代码运行在JVM上的生命周期:编译器将Java代码编译输出字节码,并保存在.class文件中。字节码是针对JVM的指令,是一种二进制格式的代码,类似于具体平台和操作系统的可执行机器码指令。将类定义的字节码装入正在运行的JVM的内存的过程称为类加载(classloading)。JVM带有类加载器(classloader),负责解析.class文件并将文件加载到内存中。类加载器是一种抽象,能从磁盘、网络接口甚至诸如JAR这类存档文件中加载类文件。类只会在运行中的JVM需要某个类的定义时被加载。一旦一个类完成了加载,JVM自己就会对字节码进行验证以确保是合法的,包括验证字节码不会分支访问到其类字节外部的内存位置,还包括检查所有的代码指令都是完整的指令。一旦完成了代码验证,JVM就可以将字节码解释为底层平台和操作系统上对应的指令代码。
  39. System.gc():System类中的静态方法gc的用途是告诉JVM运行一次垃圾回收,但是调用这个方法并不能保证一定会进行一次垃圾回收。
  40. finalize():finalize方法是继承自Object的一个project方法。当JVM要对一个对象进行垃圾回收时,会首先调用这个对象的finalize方法。这个方法的作用是完成任何未了结的事情,以及关闭被垃圾回收的对象所依赖的所有资源。
  41. java.lang.ref.WeakReference是一个泛型的容器类,当包含的实例没有强引用时,就可以被垃圾回收。
  42. Java代码是运行在线程中的。创建一个新线程之前首先要构建一个新的Thread对象。Thread对象构建的时候接受Runnable接口的一个实例。Runnable接口只有一个方法:public void run()。注意在启动线程时,千万不要自己调用这个run方法,而是应该调用线程对象的start方法,当JVM创建好了运行代码的新线程之后会转而调用run方法。
  43. Thread和Executor的区别。Executor接口是针对并行计算的一种抽象,Executor实例负责管理带缓存的线程池。Executor允许并发代码以一种受管的方式运行,接口中有一个方法:void execute(Runnable)。通过使用线程池,可以在需要线程的时候复用已经运行完其他代码的线程,而不是创建新的线程。Executors.newCachedThreadPool返回的不是一个Executor,而是一个ExecutorService。ExecutorService的主要优势在于并行计算可以返回一个结果,还支持关闭操作,而Executor使用的Runnable对象只有一个void返回类型。

    public class UsingExecutor {
    public static void main(String args[]) {
        final Executor executor = Executors.newCachedThreadPool();
        executor.execute(new ThreadPrinter());
        executor.execute(new ThreadPrinter());
    }
    }
    public class ThreadPrinter implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("From new thread: " + Thread.currentThread().getName());
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        }
    }
    }
  44. 每一个Java对象都能给上锁。如果将代码块包装在synchronized(object)中,那么同一时刻只能有一个线程运行这个代码块。被synchronized代码块标记的对象被当成锁使用。
  45. 当x++和x—这样的语句在编译的时候,生成的字节码中包含了3个操作:将x中保存的值从内存中取出来,将这个值递增1,然后将x值写回内存。在这3个操作之间,JVM可能会中断操作,而切换至的线程可能会修改x值。如果发生这种情况,那么当JVM切换回原来的线程时,写回内存的值就是错误的,因此被中断的线程得到的就是错误的值。
  46. 使用不可变对象的一个主要优势在于:由于对象的值永远不可变化,因此可以随意在线程之间传递,而不需要使用锁。当需要更新某个不可变对象中的值时,通常都需要生成一份带有更新值的新拷贝。
  47. 根据类名创建一个实例:
    Class c = Class.forName(java.util.TreeSet);
    Method m = c.getMethod("getClass"); //Sigleton有一个方法为print  
    System.out.println(m.invoke(c.newInstance()));; //调用print方法 打印: java.util.TreeSet

数据库
  1. SQL(Structured Query Language)是一种抽象的声明式语言,
  2. 连接(join)是指两个或多个表上的查询。在查询的JOIN子句中,如果指定的是LEFT OUTER JOIN (左外连接),那么结果中会包含来自查询中左侧的所有行,在右侧没有匹配的地方用NULL填补。RIGHT OUTER JOIN (右外连接)则是相反的操作。左外连接和右外连接还可以同时进行,在两个表中对于不存在的项都填入NULL,使用的语言是FULL OUTER JOIN。
  3. JOIN:

    SELECT * FROM employees e JOIN office_locations o ON e.office_ id=o.office_id;
  4. COUNT:

    SELECT COUNT(*) FROM tablename;
  5. SUM:

    SELECT SUM(annual_salary) FROM salaries;
  6. GROUP BY:

    SELECT e.title, avg(s.annual_salary) FROM employees e JOIN salaries s ON e.employee_number=s.employee_number GROUP BY e.title;
  7. INSERT:

    INSERT INTO employees VALUES (1, ‘Bob’, ‘CEO’, ‘1, Pine Drive’, CURDATE(), 1);
  8. UPDATE:

    UPDATE employees SET address=’37, Street 1’ WHERE employee_number=6;
  9. DELETE:

    DELETE FROM employees WHERE name=’Bob’;
  10. 视图(view):将一个常用的查询或连接设置成一个“虚拟表”,这个虚拟表称为视图。

    CREATE VIEW employees_and_location AS
    SELECT employee_number, name, location_name, address
    FROM employees
    JOIN office_locations
    ON employees.office_id=office_locations.office_id;
  11. DDL和DML。数据操纵语言(Data Manipulation Language, DML)通过SELECT、INSERT、UPDATE和DELETE关键字操纵数据。数据定义语言(Data Definition Language, DDL)的用途则是创建和操纵表的结构。
  12. CREATE TABLE :

    CREATE TABLE meeting_rooms (
    meeting_room_id     INT,
    office_id               INT,
    meeting_room_name   VARCHAR(100)
    );
  13. ALTER TABLE :

    ALTER TABLE meeting_rooms ADD COLUMN telephone VARCHAR(100);
    ALTER TABLE meeting_rooms DROP COLUMN telephone;
    ALTER TABLE meeting_rooms ADD FOREIGN KEY (office_id) 
    REFERENCES office_locations(office_id);
  14. 创建了索引之后,向表中插入数据时还会在索引中创建一个条目,可能会导致插入数据耗费的时间更长。带来的好处是查询更快,但需要更多的磁盘空间来保存这些索引。
  15. 存储过程(stored procedure)可以执行更多的过程式步骤,提供的功能比SQL提供的简单创建、读取、更新和删除功能更为强大,特别适合于批量操作一系列语句。
  16. CREATE PROCEDURE:
CREATE PROCEDURE annual_salary_raise(
    IN percent INT,
    OUT total_updated INT
)
BEGIN
SELECT COUNT(*) INTO total_updated FROM salaries
        WHERE last_update_date > DATE_SUB(CURDATE(), INTERVAL 1 YEAR);

UPDATE salaries
SET annual_salary=annual_salary*((100+percent)/100),last_update_date=CURDATE()
        WHERE last_update_date > DATE_SUB(CURDATE(), INTERVAL 1 YEAR);
    END;
  1. 触发器(trigger)的一个实际用法是记录数据库事件。当某个特定的数据库事件发生时,例如删除或更新一行数据时,可以配置在动作发生之前或之后运行一个存储过程,也可以用一个存储过程替代相应的动作。
  2. 事务(transaction)必须满足的4个条件(ACID):
    原子性(Atomic)——事务中的内容要么全做,要么全不做。
    一致性(Consistent)——执行完事务之后,数据库满足作用域的所有要求。
    隔离性(Isolated)——每一个并行运行的事务其行为看上去都应该好像只有这一个事务在数据库中运行。
    持久性(Durable)——事务一旦被提交之后,就永久地提交了。
  3. NoSQL指的是所有不严格遵循关系型数据库模型的数据库。如MongoDB、Cassandra、Memcached、Redis等。
  4. JDBC(Java Database Connectivity)是构建在Java标准库中的数据库连接机制。
  5. 使用查询参数(query parameter)可以让数据库编译查询,并对查询做好预处理,然后只要提供参数就可以执行实际的查询。通过问号表示查询调用要使用的变量参数,使用PreparedStatement对象,在每一次执行查询时,通过setXXX方法设置参数,指明查询中对应数据的类型,而不是将参数值硬编码为String,可以实现一定程序的安全性,避免类似SQL注入这类攻击。

Web应用
  1. Web应用程序在Servlet API中的定义
    Servlet API通过一个名为web.xml的部署描述文件(deployment descriptor)来定义Web应用程序,这个文件位于classpath的/webapp/WEB-INF/web.xml路径下。这个文件定义了Servlet,以及通过Servlet容器进行配置和服务的方式。web.xml中Servlet都被映射到URL模式,表示具体的请求(根据请求的路径判定)由哪一个Servlet处理。
  2. WAR(Web Archive)文件特别适合于Web应用程序的打包,不仅包含运行应用程序所需的所有编译后的类文件,还包含提供所有属性及其他配置文件。
  3. Tomcat由多个组件构成,有一个Catalina组件是servlet容器,还有一个Jasper组件负责处理JSP。
  4. HttpServletRequest有一个名为getParameterMap的方法,这个方法将请求中的参数键-值对以映射的形式返回。由于在HTTP请求中,一个键对应的参数可以有多个值,因此这个映射的类型为Map<String, String[]>。
  5. HTTP方法,有时候称为动作(verb),是一种指令,用途是告诉Web服务器针对请求的资源采取什么操作。服务器实现的常见方法包括:
    GET——返回请求的资源,不能做任何修改。
    POST——根据请求的内容更新资源。
    PUT——将资源的内容设置为请求的内容。
    DELETE——删除请求的资源。
    HEAD——类似GET请求,但是只返回应答码和应答头。
  6. HTTP应答的第一行包含一个应答码以及一条描述应答码含义的短小信息。应答码第一个表示应答的分类。
  7. 2xx的应答码表示成功的请求和应答
    200 OK——表示一次成功的请求。
    201 Created——通常返回自PUT请求,表示请求成功,并且已经创建资源。
    204 No Content——请求成功,服务器没有更多信息可以发送给客户端,通常用于成功的PUT、POST和DELETE请求。
  8. 4xx类的错误码告诉客户端发生了错误,表示服务器没有完成一次成功的请求
    400 Bad Request——客户端发送的请求格式不对。
    403 Forbidden——通常表示客户端提供了正确的登录,但是没有权限访问请求的资源。
    404 Not Found——请求的资源不存在。
    405 Method Not Allowed——通过错误的HTTP方法请求了一项资源。
  9. 5xx类的错误码表示服务端发生了错误,客户端无法恢复,需要对服务器进行修改
    500 Internal Server Error——表示服务器上某一项资源或某一组资源有问题,而不是整个服务器不可用。
    503 Service Unavailable——服务器目前不可用,表示服务器暂时停止服务。
  10. Serializable接口。对象序列化是一种将Java对象从JVM中“导出”的简单方法:序列化的数据不一定要写入网络,也可以写入磁盘或其他I/O接口。
  11. transient关键字。如果在一个Serializable对象中,在将数据写入流时有一些字段不要写入,那么可以在相应的字段声明前面加上transient修饰符。当transient字段被反序列化时,会被设置为null。transient字段的主要使用场景包括用于缓存的私有字段(在反序列化时可以简单地重新生成缓存),以及反序列化时可以重新构造的数据(例如通过其他字段创建的字段)。
  12. JSON(JavaScript Object Natation)是另一种序列化方法,可以被多种语言解析和处理。
  13. POJO(Plain Old Java Object)指的是没有不必要约束的类,例如不需要为持久化框架而实现某个接口,或为了编写Web应用程序而扩展某个抽象类。
  14. Spring允许开发都编写POJO,然后借助依赖注入的方式,通过一个名为应用上下文(application context)的核心组件将它们连接起来。
  15. 依赖注入是Spring的核心特性之一。通过依赖注入可以移除一个类可能对其他类或第三方接口的特定依赖,并且在构造时将这些依赖加载到类中。使用依赖注入的好处在于可以修改被依赖类的实现,而不需要修改自己的类的代码,从而可以更容易实现分享的测试。甚至可以重写依赖项的实现而不需要修改自己的类,只要接口没有变化。
  16. 模型-视图-控制器(Model-View-Controller, MVC)模式是用户界面常用的方法。
    模型,表示的是应用程序使用的数据。
    视图,表示模型在用户面前的展现形式,可以是屏幕上的文本,也可以是利用图像和动画显示的更为复杂的可视化效果。
    控制器,将所有组件连接起来,负责处理所有的输入,这将控制模型的创建,然后负责将这个模型转交给合适的视图。
  17. Hibernate是一个专门用来管理Java对象和数据库表之间映射的工具,这种映射通常称为Object/Relational Mappling,简称ORM。
  18. hibernate.cfg.xml文件中包含数据库及其配置信息和指向映射文件的引用。

计算机网络
  1. 局域网甲内的主机A开启了P2P下载工具(如BT、eMule等),它通过NAT穿越技术同局域网乙中的主机B建立连接。
    NAT(Network Address Translation,网络地址转换)是一种将一个IP地址域映射到另一个IP地址域的技术,从而为终端主机提供透明路由。NAT包括静态网络地址转换、动态网络地址转换、网络地址及端口转换、动态网络地址及端口转换、端口映射等。NAT常用于私有地址域与公用地址域的转换,以解决IP地址匮乏问题。在防火墙上实现NAT后,可以隐藏受保护网络的内部拓扑结构,在一定程度上提高网络的安全性。如果反向NAT提供动态网络地址及端口转换功能,还可以实现负载均衡等功能。
  2. FTP是有连接的服务,基于TCP协议。
  3. 在一个IP数据包到达目的地址之前,它不可能重组,但可能成为碎片。
  4. C类网络的子网中IP为0~255,其中0和255不能用,所以最多能容纳254个主机。
  5. 以太网(Ethernet)转换控制包是基于目的IP地址。
  6. SYN包是TCP连接的第一个包,是非常小的一种数据包。SYN攻击包括大量此类的包,由于这些包看上去来自实际不存在的站点,因此无法有效地进行处理。SYN攻击就是利用TCP连接的三次握手机制,发起攻击端只来一、二次握手,而被攻击端就一直在试图完成TCP连接,造成资源不足。
  7. 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接:
    第一次握手:建立连接时,客户端发送SYN包(SYN=j)到服务器,并进入SYN_SEND状态,等待服务器确认。
    第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
  8. 三次握手过程中还有一些重要概念:
    1) 未连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(SYN=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于SYN_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。
    2) SYN:同步标志。同步序列编号(Synchronize Sequence Numbers)栏有效,该标志公在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。
    3)
    ACK:确认标志。确认编号(Acknowledgement Number)栏有效。
    4) RST:复位标志。用于复位相应的TCP连接。
    5)
    URG:紧急(The urgent pointer)标志。
    6) PSH:推标志。该标志置位时,接收端不将该数据进行队列处理,而是尽可能快地将数据转由应用处理。在处理telnet或rlogin等交互模式的连接时,该标志总是置位的。
    7)
    FIN:结束标志。带有该标志置位的数据包用来结束一个TCP会话,但对应端口仍处于开放状态,准备接收后续数据。

不定期更新,如有总结不当之处,请指正或补充!

5人推荐
随时随地看视频
慕课网APP