一次失败的面试——学会总结

来源:互联网 发布:淘宝直通车助手软件 编辑:程序博客网 时间:2024/04/30 22:12

虽然能耐不大,好歹是写了一年代码的被公司认可为中级软件工程师的,但是新近的面试,被问成狗了大哭快哭了

公司的面试问题不可能是太冷门,那只能是我的基础能忘则忘了,信心被打击不要紧,但是要懂得从失败的地方爬起来!

所以,面试过的问题,都是拿来总结经验,走向成功的敲门砖。

注:问题答案均采自网络,感谢各位作者


1.error和exception

关键字: error ,exception

Error类和Exception类都继承自Throwable类。

  • Error的继承关系:

java.lang.Object 

java.lang.Throwable

java.lang.Error

  • Exception的继承关系:

java.lang.Object

java.lang.Throwable

java.lang.Exception

 

二者的不同之处:

Exception:

1.可以是可被控制(checked) 或不可控制的(unchecked)

2.表示一个由程序员导致的错误

3.应该在应用程序级被处理

 

Error:

1.总是不可控制的(unchecked)

2.经常用来用于表示系统错误或低层资源的错误

3.如何可能的话,应该在系统级被捕捉


Java 中定义了两类异常: 

  1) Checked exception: 这类异常都是Exception的子类 。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。 

  2) Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们不能通过client code来试图解决,所以称为Unchecked exception 。


 Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题,比如,内存资源不足等,对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。

 Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。

     Exception又分为运行时异常,受检查异常。

     运行时异常,表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。

     受检查异常,是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之为受检查异常。


异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它。Java中使用异常类来表示异常,不同的异常类代表了不同的异常。但是在Java中所有的异常都有一个基类,叫做Exception。错误:它指的是一个合理的应用程序不能截获的严重的问题。大多数都是反常的情况。”,错误是VM的一个故障(虽然它可以是任何系统级的服务)。所以,错误是很难处理的,一般的开发人员(当然不是你)是无法处理这些错误的。比如内存溢出;和异常一样,在Java中用错误类来表示错误,不同的错误类代表了不同的错误。但是在Java中所有的错误都有一个基类,叫做Error。综上,我们可以知道异常和错误最本质的区别就是异常能被开发人员处理而错误时系统本来自带的,一般无法处理也不需要我们程序员来处理。


常见的java Exception(runtimeException )几种如下:

NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常

算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException

java.lang.AbstractMethodError
抽象方法错误。当应用试图调用抽象方法时抛出。

java.lang.AssertionError
断言错。用来指示一个断言失败的情况。

java.lang.ClassCircularityError
类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。

java.lang.ClassFormatError
类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。

java.lang.Error
错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。

java.lang.ExceptionInInitializerError
初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。

java.lang.IllegalAccessError
违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。

java.lang.IncompatibleClassChangeError
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。

java.lang.InstantiationError
实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.

java.lang.InternalError
内部错误。用于指示Java虚拟机发生了内部错误。

java.lang.LinkageError
链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。

java.lang.NoClassDefFoundError
未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。

java.lang.NoSuchFieldError
域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。

java.lang.NoSuchMethodError
方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。

java.lang.OutOfMemoryError
内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。

java.lang.StackOverflowError
堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。

java.lang.ThreadDeath
线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。

java.lang.UnknownError
未知错误。用于指示Java虚拟机发生了未知严重错误的情况。

java.lang.UnsatisfiedLinkError
未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。

java.lang.UnsupportedClassVersionError
不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。

java.lang.VerifyError
验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。

java.lang.VirtualMachineError
虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。

java.lang.ArithmeticException
算术条件异常。譬如:整数除零等。

java.lang.ArrayIndexOutOfBoundsException
数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

java.lang.ArrayStoreException
数组存储异常。当向数组中存放非数组声明类型对象时抛出。

java.lang.ClassCastException
类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。

java.lang.ClassNotFoundException
找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

java.lang.CloneNotSupportedException
不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。

java.lang.EnumConstantNotPresentException
枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。

java.lang.Exception
根异常。用以描述应用程序希望捕获的情况。

java.lang.IllegalAccessException
违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。

java.lang.IllegalMonitorStateException
违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。

java.lang.IllegalStateException
违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。

java.lang.IllegalThreadStateException
违法的线程状态异常。当县城尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。

java.lang.IndexOutOfBoundsException
索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。

java.lang.InstantiationException
实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。

java.lang.InterruptedException
被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。

java.lang.NegativeArraySizeException
数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。

java.lang.NoSuchFieldException
属性不存在异常。当访问某个类的不存在的属性时抛出该异常。

java.lang.NoSuchMethodException
方法不存在异常。当访问某个类的不存在的方法时抛出该异常。

java.lang.NullPointerException
空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。

java.lang.NumberFormatException
数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。继承IllegalArgumentException,字符串转换为数字时出现,比如 int i=Integer.ParseInt("ab3").

java.lang.RuntimeException
运行时异常。是所有Java虚拟机正常操作期间可以被抛出的异常的父类。

java.lang.SecurityException
安全异常。由安全管理器抛出,用于指示违反安全情况的异常。

java.lang.StringIndexOutOfBoundsException
字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。

java.lang.TypeNotPresentException
类型不存在异常。当应用试图以某个类型名称的字符串表达方式访问该类型,但是根据给定的名称又找不到该类型是抛出该异常。该异常与ClassNotFoundException的区别在于该异常是unchecked(不被检查)异常,而ClassNotFoundException是checked(被检查)异常。

java.lang.UnsupportedOperationException
不支持的方法异常。指明请求的方法不被支持情况的异常。


2.final、finally、finalize的意义

final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。 finally—在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。 finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
更详细参见http://blog.csdn.net/zhangweikai966/article/details/5954955

一、性质不同

(1)final为关键字;

(2)finalize()为方法;

(3)finally为为区块标志,用于try语句中;

二、作用

(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中;

(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);

(3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;

三、final详解

1定义变量

1.1  final定义基本类型变量时,要求变量初始化必须在声明时或者构造函数中,不能用于其它地方。该关键字定义的常量,除了初始化阶段,不能更改常量的值。

1.2  final定义对象的引用,该引用的初始化与定义常量时的要求一致;该关键字定义的对象内容可以改变,但是引用指向的地址不能改变;

2定义参数

如果传入该参数定义的变量时,与定义变量的修改规则相同;java方法中传递基本类型时是传值的,java方法对于对象的传递是传参的;<归根结底,java中方法的传递是依靠传递“副本”:对于基本类型,首先建立一个Copy,并将传入的赋值给Copy,然后对Copy进行操作;对于对象类型,首先建立一个引用Copy,并将传入的对象引用赋值给Copy>

比如:method(final int test);

3定义方法

(1)使用final关键字定义的方法,不能被子类继承;

(2)允许编译器将所有对此方法的调用转化为inline(行内)行为,即可以将此方法直接复制在调用处,而不是进行例行的方法调用(保存断点、压栈),这样会使程序的效率升高。但是---------如果过多的话,这样会造成代码膨胀,反而会影响效率,所以该方法要慎用。。

4定义类

一个任何final类无法被任何人继承,这也就意味着此类在一个继承树中是一个叶子类,并且此类被认为是很完美的,不需要进行任何修改(总之是不推荐使用)


3.overload和override

override(重写,覆盖) 
1、方法名、参数、返回值相同。 
2、子类方法不能缩小父类方法的访问权限。 
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 
4、存在于父类和子类之间。 
5、方法被定义为final不能被重写。 

overload(重载,过载) 
1、参数类型、个数、顺序至少有一个不相同。   
2、不能重载只有返回值不同的方法名。 
3、存在于父类和子类、同类中。 

方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现。 
重写(Overriding)是父类与子类之间多态性的一种表现,而重载(Overloading)是一个类中多态性的一种表现。 

如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了. 

如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型或有不同的参数次序,则称为方法的重载(Overloading)。不能通过访问权限、返回类型、抛出的异常进行重载. 

1. Override 特点 
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果; 
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致; 
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类; 
4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。 

2.Overload 特点 
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int, float), 但是不能为fun(int, int)); 
2、不能通过访问权限、返回类型、抛出的异常进行重载; 
3、方法的异常类型和数目不会对重载造成影响; 
4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

4.有一组数1..n,随机放在一个大小为n的一维数组中,实现一个排序函数对其进行排序并返回。

int[] nums = { 1,2,3,4,6,7,5,8,9 }; 
public int[]  SortArray (int[] nums ) {
  // 第一种方法 直接用API提供的方法默认是升序排列  Arrays.sort(nums);  for (int num : nums) {   System.out.print(num + " ");  }  System.out.println();
  // 第二种方法 自己写 冒泡排序
 nums = new int[] { 1,2,3,4,6,7,5,8,9 }; 
for (int i = 0; i < nums.length; i++) { for (int m = i; m < nums.length; m++) { if (nums[i] > nums[m]) { int temp = nums[i]; nums[i] = nums[m]; nums[m] = temp; } } } for (int num : nums) { System.out.print(num + " "); } }

对于基本数据类型只要Arrays.sort(数组)[“注:数组是声明为基本数据类型的数组,如int[]等”]
对于对象类型,要 implement Comparable,所以得重载 compareTo() 这个方法。有了这个方法,那么 Arrays.sort() 就能依照这个方法的回传值来作排序的依据。事实上,基本数据类型也都有 implement Comparable 的,所以才能够这样子使用。一般而言,我们习惯的排序结果是由小到大,所以在 compareTo() 当中「大于」是回传正值。


5.写出一个singleton模式的实现,即如果需要实现在一个进程内某个类的对象唯一的实现。

概念:
  java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。

public class Singleton {      private static Singleton uniqueInstance = null;         private Singleton() {         // Exists only to defeat instantiation.      }         public static Singleton getInstance() {         if (uniqueInstance == null) {             uniqueInstance = new Singleton();         }         return uniqueInstance;      }      // Other methods...  }  

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上实现没有考虑线程安全问题。所谓线程安全是指:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。

1.饿汉式单例类

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //饿汉式单例类.在类初始化时,已经自行实例化   
  2. public class Singleton1 {  
  3.     //私有的默认构造子  
  4.     private Singleton1() {}  
  5.     //已经自行实例化   
  6.     private static final Singleton1 single = new Singleton1();  
  7.     //静态工厂方法   
  8.     public static Singleton1 getInstance() {  
  9.         return single;  
  10.     }  
  11. }  


2.懒汉式单例类

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //懒汉式单例类.在第一次调用的时候实例化   
  2. public class Singleton2 {  
  3.     //私有的默认构造子  
  4.     private Singleton2() {}  
  5.     //注意,这里没有final      
  6.     private static Singleton2 single=null;  
  7.     //静态工厂方法   
  8.     public synchronized  static Singleton2 getInstance() {  
  9.          if (single == null) {    
  10.              single = new Singleton2();  
  11.          }    
  12.         return single;  
  13.     }  
  14. }  

6.父类和子类的构造函数

使用构造器时需要记住:

1.构造器必须与类同名(如果一个源文件中有多个类,那么构造器必须与公共类同名)

2.每个类可以有一个以上的构造器

3.构造器可以有0个、1个或1个以上的参数

4.构造器没有返回值

5.构造器总是伴随着new操作一起调用


使用super调用父类构造器的语句必须是子类构造器的第一条语句

如果子类构造器没有显式地调用父类的构造器,则将自动调用父类的默认(没有参数)的构造器。如果父类没有不带参数的构造器,并且在子类的构造器中又没有显式地调用父类的构造器,则java编译器将报告错误。

示例:

A.java

Java代码  收藏代码
  1. public class A{  
  2.    public A(){  
  3.       System.out.println("调用了A的无参构造函数");  
  4.    }  
  5.    public A(String mess){  
  6.       System.out.println("调用了A的有参的构造函数\n"+  
  7.          "参数内容为:"+mess);  
  8.    }  
  9. }  

 

B.java

Java代码  收藏代码
  1. public class B extends A{  
  2.    public B(){  
  3.       System.out.println("调用了B的无参构造函数");  
  4.    }  
  5.    public B(String mess){  
  6.       super(mess);  
  7.       System.out.println("调用了B的有参构造函数\n"+  
  8.          "参数内容为:"+mess);  
  9.    }  
  10. }  

 

Test.java

Java代码  收藏代码
  1. public class Test{  
  2.    public static void main(String [] args){  
  3.        B b_01=new B();  
  4.        B b_02=new B("你好");  
  5.    }  
  6. }  

 

输出结果:


7.多线程和单线程

多线程有两种实现方法, 继承Thread类;实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

线程的基本概念、线程的基本状态及状态之间的关系?

线程,有时称为轻量级进程,是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。

线程有四种状态:新生状态、可运行状态、被阻塞状态、死亡状态。状态之间的转换如下图所示:

线程与进程的区别?

1、 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。2、 一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个进程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。3、 系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。那就是说,出了CPU之外(线程在运行的时候要占用CPU资源),计算机内部的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。4、 与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。5、 进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。


多线程同步和互斥有几种实现方法,都是什么?

线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。



多线程同步和互斥有何异同,在什么情况下分别使用他们?

线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。


8.同步和异步

java 异步与同步应用

所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回。所以异步的同义语是非阻塞(None Blocking)。

普通B/S模式(同步)AJAX技术(异步)              同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事              异步:   请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)

A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

B.每个对象只有一个锁(lock)与之相关联。

C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


ArrayList 和HashMap是异步,Vector和HashTable是同步,这里同步是线程安全的,异步不是线程安全的,举例说明: 

当创建一个Vector对象时候, 

Vector ve=new Vector(); 
ve.add("1"); 

当在多线程程序中,第一个线程调用修改对象ve的时候,就为其上了锁,其他线程只有等待。 

当创建一个ArrayList对象时候, 

ArrayList list=new ArrayList(); 
list.add("1"); 

当在多线程程序中,第一个线程调用修改对象list的时候,没有为其上锁,其他线程访问时就会报错。 

eg:list.remove("1"),然后再由其他线程访问list对象的1时就会报错。 


9.冒泡排序算法、选择排序算法

冒泡排序算法的基本思想是:首先将第n-1个记录的关键字和第n个记录的关键字进行比较,若为“逆序”(即L.r[n].key<L.r[n-1].key),则将两个记录交换之,然后比较第n-2个记录和第n-1个记录的关键字。依次类推,直至第1个记录的关键字和第2个记录的关键字比较过为止。这是第一趟起泡排序,其结果是使得关键字最小的记录被安置到第一个记录的位置上;然后进行第二趟起泡排序,对后面的n-1个记录进行同样的操作,其结果是使关键字次小的记录被安置到第2个记录的位置;一般地,第i趟起泡排序是从L.r[n] 到L.r[i]依次比较相邻两个记录的关键字,并在“逆序”时交换相邻记录,其结果是这n-i+1个记录中关键字最小的记录被交换到第i的位置上。整个排序过程需要进行K(1≤k<n)趟起泡排序,显然,判别起泡排序结束的条件应该是“在一趟排序过程中没有进行过交换记录的操作”。

    以下是冒泡排序的JAVA代码实现:

[java] view plaincopy
  1. package cn.simon.sort;  
  2. public class BubbleSort {  
  3.     public static <T extends Comparable<? super T>> void bubbleSort(T[] array) {  
  4.         for (int i = 0; i < array.length; i++) {  
  5.             for (int j = array.length - 1; j > i; j--) {  
  6.                 if (array[j].compareTo(array[j - 1]) < 0) {  
  7.                     T temp = array[j];  
  8.                     array[j] = array[j - 1];  
  9.                     array[j - 1] = temp;  
  10.                 }  
  11.             }  
  12.         }  
  13.     }  
  14.       
  15.     public static void main(String[] args) {  
  16.         Integer[] testArray = {2325124235};  
  17.         BubbleSort.bubbleSort(testArray);  
  18.         System.out.println("The result is:");  
  19.         for (Integer item : testArray) {  
  20.             System.out.print(item);  
  21.             System.out.print(' ');  
  22.         }  
  23.     }  
  24. }  
 

 直接插入排序算法的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。

把a[i]插入到a[0],a[1],...,a[i-1]之中的具体实施过程为:先把a[i]赋值给变量t,然后将t依次与a[i-1],a[i-2],...进行比较,将比t大的元素右移一个位置,直到发现某个j(0<=j<=i-1),使得a[j]<=t或j为(-1),把t赋值给a[j+1]。

    以下是直接插入排序算法的JAVA代码实现:

[java] view plaincopy
  1. package cn.simon.sort;  
  2. public class InsertSort {  
  3.     public static <T extends Comparable<? super T>> void insertSort(T[] array) {  
  4.         for (int i = 1; i <= array.length - 1; i++) {  
  5.             int j = i;  
  6.             while (j>=1 && array[j].compareTo(array[j - 1]) < 0) {  
  7.                 T temp = array[j];  
  8.                 array[j] = array[j - 1];  
  9.                 array[j - 1] = temp;  
  10.                   
  11.                 j--;  
  12.             }  
  13.         }  
  14.     }  
  15.       
  16.     public static void main(String[] args) {  
  17.         Integer[] testArray = {2325124235};  
  18.         InsertSort.insertSort(testArray);  
  19.         System.out.println("The result is:");  
  20.         for (Integer item : testArray) {  
  21.             System.out.print(item);  
  22.             System.out.print(' ');  
  23.         }  
  24.     }  
  25. }  

 直接选择排序算法的基本思想是:n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果:

        ①初始状态:无序区为R[1..n],有序区为空。

        ②第1趟排序

        在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。

        ……

        ③第i趟排序

        第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R[i..n](1≤i≤n-1)。该趟排序从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,使R[1..i]和R[i+1..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。

        这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果。

    以下是直接选择排序算法的JAVA代码实现:

[java] view plaincopy
  1. package cn.simon.sort;  
  2. public class SelectSort {  
  3.     public static <T extends Comparable<? super T>> void selectSort(T[] array) {  
  4.         for (int i = 0; i <= array.length - 2; i++) {  
  5.             int lowestIndex = i;  
  6.             for (int j = array.length - 1; j > i; j--) {  
  7.                 if (array[j].compareTo(array[lowestIndex]) < 0) {  
  8.                     lowestIndex = j;  
  9.                 }  
  10.             }  
  11.               
  12.             T temp = array[i];  
  13.             array[i] = array[lowestIndex];  
  14.             array[lowestIndex] = temp;  
  15.         }  
  16.     }  
  17.       
  18.     public static void main(String[] args) {  
  19.         Integer[] testArray = {2325124235};  
  20.         SelectSort.selectSort(testArray);  
  21.         System.out.println("The result is:");  
  22.         for (Integer item : testArray) {  
  23.             System.out.print(item);  
  24.             System.out.print(' ');  
  25.         }  
  26.     }  
  27. }  

快速排序算法的基本思想是:设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:

    ①分解:在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。

    注意:划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(注意pivot=R[pivotpos]):R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys  其中low≤pivotpos≤high。

    ②求解:通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。

    ③组合:因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。

    以下是快速排序算法的JAVA代码实现:

[java] view plaincopy
  1. package cn.simon.sort;  
  2. public class QuickSort {  
  3.     public static <T extends Comparable<? super T>> void quickSort(T[] array, int low, int high) {  
  4.         if (low < high) {  
  5.             int pivotPos = partition(array, low, high);  
  6.               
  7.             quickSort(array, low, pivotPos - 1);  
  8.             quickSort(array, pivotPos + 1, high);  
  9.         }  
  10.     }  
  11.       
  12.     private static <T extends Comparable<? super T>> int partition(T[] array, int low, int high) {  
  13.         T pivot = array[low];  
  14.           
  15.         while (low < high) {  
  16.             while (low < high && array[high].compareTo(pivot) > 0) {  
  17.                 high--;  
  18.             }  
  19.               
  20.             if (low < high) {  
  21.                 array[low++] = array[high];  
  22.             }  
  23.               
  24.             while (low < high && array[low].compareTo(pivot) < 0) {  
  25.                 low++;  
  26.             }  
  27.               
  28.             if (low < high) {  
  29.                 array[high--] = array[low];  
  30.             }  
  31.         }  
  32.           
  33.         array[low] = pivot;  
  34.           
  35.         return low;  
  36.     }  
  37.       
  38.     public static void main(String[] args) {  
  39.         Integer[] testArray = {2325124235};  
  40.         QuickSort.quickSort(testArray, 0, testArray.length - 1);  
  41.         System.out.println("The result is:");  
  42.         for (Integer item : testArray) {  
  43.             System.out.print(item);  
  44.             System.out.print(' ');  
  45.         }  
  46.     }  
  47. }  

希尔排序算法的基本思想是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。

    以下是希尔排序算法的JAVA代码实现:

[java] view plaincopy
  1. package cn.simon.sort;  
  2. public class ShellSort {  
  3.     public static <T extends Comparable<? super T>> void shellSort(T[] array, int len) {  
  4.         int d = len;  
  5.         while (d > 1) {  
  6.             d = (d + 1) / 2;  
  7.             for (int i = 0; i < len - d; i++) {  
  8.                 if (array[i + d].compareTo(array[i]) < 0) {  
  9.                     T temp = array[i + d];  
  10.                     array[i + d] = array[i];  
  11.                     array[i] = temp;  
  12.                 }  
  13.             }  
  14.         }  
  15.     }  
  16.       
  17.     public static void main(String[] args) {  
  18.         Integer[] testArray = {2325124235};  
  19.         ShellSort.shellSort(testArray, 5);  
  20.         System.out.println("The result is:");  
  21.         for (Integer item : testArray) {  
  22.             System.out.print(item);  
  23.             System.out.print(' ');  
  24.         }  
  25.     }  
  26. }  

10.抽象类

在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。好比,动物是一个抽象类,人、猴子、老虎就是具体实现的派生类,我们就可以用动物类型来隐藏人、猴子和老虎的类型。
Java中的接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口是一种特殊形式的抽象类。


抽象类和接口有很大的区别,首先,抽象类在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个接口。
其次,在抽象类的定义中,我们可以赋予方法的默认行为。但是在接口的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会 增加一些复杂性,有时会造成很大的麻烦。
已经提到过,抽象类在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在”is a”关系,即父类和派生类在概念本质上应该是相同的。对于接口来说则不然,并不要求接口的实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已。接口表示的是”like a”关系。

使用抽象类来定义允许多个实现的类型,比使用接口有一个明显的优势:抽象类的演化比接口的演化要容易的多。在后续的发行版中,如果希望在抽象类中增加一个方法,只增加一个默认的合理的实现即可,抽象类的所有实现都自动提供了这个新的方法。对于接口,这是行不通的。


抽象类中必须有抽象方法,同时也可以有非抽象方法,即可以有方法的具体实现,继承抽象父类的子类中,如果子类没有实现抽象父类中的抽象方法,那么这个子类也必须声明为抽象的,即只要类中有抽象的方法那么这个类就一定是抽象类,但是抽象类中的方法不一定都是抽象方法,只是至少有一个是抽象方法即可

java中接口可以继承接口吗?

java中不允许类多重继承的主要原因是:如果A同时继承B和C,而假如B和C同时有一个d方法,A如何决定该继承哪一个呢? 
但接口不存在这样的问题,接口中全都是抽象方法,继承谁都无所谓,所以接口可以继承多个接口。 
interface b{} 
interface c{} 
interface a extends b,c{}


抽象方法:在类中没有方法体的方法,就是抽象方法。

抽象类:含有抽象方法的类就叫抽象类。


抽象类中的抽象方法必须被实现!
如果一个子类没有实现父类中的抽象方法,则子类也成为了一个抽象类!
抽象类中的普通方法,可以不必实现。


1 用abstract关键字来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法。例如 :
abstract class Animal { //用abstract来定义一个Animal为抽象类
}
public abstract void enjoy(); //用abstract来定义一个抽象方法"enjoy"

含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被重写。

抽象类不能被实例化。

抽象方法只需声明,而不需实现某些功能。如:
public abstract void enjoy(); 
上面的这个抽象方法不需要实现功能,而一般的方法 :
public void enjoy() {
System.out.print("叫声"); //有必要实现某些功能
}

例子 :多态程序,没抽象类的
class Person {
public void f() {
System.out.println("父类");
}
}

class Students extends Person {
public void f() {
System.out.println("学生类");
}
}
class Teacher extends Person {
public void f() {
System.out.println("教师类");
}
}
public class Test3 {
public static void main(String[] args) {
Person s = new Students();
Person t = new Teacher();
s.f();
t.f();
}
}

抽象类的多态程序:
abstract class Person {
public abstract void f();
}

class Students extends Person {
public void f() {
System.out.println("学生类");
}
}
class Teacher extends Person {
public void f() {
System.out.println("教师类");
}
}
public class Test3 {
public static void main(String[] args) {
Person s = new Students();
Person t = new Teacher();
s.f();
t.f();
}
}



  1. 抽象类中可以有任意个抽象方法(可以没有)

  2. 抽象类中可以有任意个非抽象方法(可以没有)

  3. 抽象方法只能定义在抽象类或接口中

  4. 抽象类不可以被实例化,但可以被其他类继承

  5. 当其他类继承了抽象类后必须把抽象类中的抽象方法重写


--------------------------------------------------------------------------------------------

抽象类是可以定义有实现的行为方法,接口才是不能定义有实现的行为方法

抽象类是不能new操作,但是可以通过继承调用相应的有实现的行为方法


---------------------------------------------------------------------------------------------


接口与抽象类的区别:


1. 一个类可以实现多个接口,一个接口可以继承多个接口,一个类只能继承一个抽象类


2. 接口中属性必须是public static final的,抽象类中属性可以是变量


3. 接口中方法必须是public abstract的,抽象类中可以有不抽象的方法


4. 接口中没有构造方法,抽象类中可以有构造方法


5. 一个类实现接口必须实现接口中所有的方法,一个类继承抽象类只需重写抽象方法,其他方法可以不用重写


6. 实现接口主要作用是对类扩展功能,继承抽象类是使用共同的特征和行为


11.Java中的容器

在书写程序的时候,我们常常需要对大量的对象引用进行管理。为了实现有效的归类管理,我们常常将同类的引用放置在同一数据容器中。

由于数据容器中存放了我们随时可能需要使用到的对象引用,所以一般的数据容器要都要能能提供方便的查询、遍历、修改等基本接口功能。

早期的OOP语言都通过数组的方式来实现对引用集的集中管理和维护。

但是数组方式下,数组大小需要提前被确定,并不允许修改大小,导致其作为一种灵活的数据容器的能力的功能大为下降。

 

为了方便的利用数据容器进行引用的管理,Java中提供了丰富的数据容器以满足程序员多样化的需求。

JAVA的容器类---List,Map,Set 


Collection 
├List 
│├LinkedList 
│├ArrayList 
│└Vector 
│ └Stack 
└Set 


Map 
├Hashtable 
├HashMap 
└WeakHashMap

!其中的Vector和Stack类现在已经极少使用。

数据容器主要分为了两类:

Collection: 存放独立元素的序列。

Map:存放key-value型的元素对。


最为常用的四个容器:

LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较ArrayList类低。

ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。

HashSet: Set类不允许其中存在重复的元素(集),无法添加一个重复的元素(Set中已经存在)。HashSet利用Hash函数进行了查询效率上的优化,其contain()方法经常被使用,以用于判断相关元素是否已经被添加过。

HashMap: 提供了key-value的键值对数据存储机制,可以十分方便的通过键值查找相应的元素,而且通过Hash散列机制,查找十分的方便。



1、Java的容器类有两种基本类型:Collection和Map,区别在于容器中每个位置保存的元素个数。Collection每个位置只能保存一个元素,Map保存的是键值对。

2、迭代器,是一个对象,它的工作是遍历并选择序列中的对象,“轻量级”的对象,创建它的代价小。常用方法:next();hasNext();

3、Collection:
(1)List:
     ArrayList:允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢,可以理解为容量大小可变的数组。
     LinkedList:向List中间插入与移除的开销并不大,随机访问则相对较慢,可以当作堆栈、队列和双向队列使用。
     最佳做法是将ArrayList作为默认首选,只有当程序性能因为经常从表中间进行插入和删除变差时,才选择LinkedList。

(2)Set,不保存重复的元素:
     HashSet:为快速查找而设计的Set,存入HashSet的对象必须定义hashCode()。
     TreeSet:保持次序的Set,底层为树结构,使用它可以从Set中提取有序的序列,“按对象比较函数对元素排序”,而不是指“元素插入的次序”。
     LinkedHashSet:具有HashSet的查询速度,并且内部使用链表维护元素的顺序(插入的次序),于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
     HashSet性能总是比TreeSet好。TreeSet存在的唯一原因是它可以维持元素的排序状态。

4、Map:
(1)HashMap:基于散列表的实现。插入和查询“键值对”的开销是固定的。
(2)LinkedHashMap:类似于HashMap,但是迭代器遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)次序。比HashMap慢一点,但是迭代访问时会更快。
(3)TreeMap:基于红黑树的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或Comparator决定),所得到的结果是经过排序的,而且是唯一带有subMap()方法的Map,可以返回一个子树。
     首选HashMap,只有在需要一个总是排好序的Map时,才使用TreeMap。

替换Map集合中的键和值:可直接put(key , value),如果key相同的话,会覆盖原来的键值对,如果key值不相同,则需要remove之前的键值对


5、如果要使用散列的数据结构(HashSet,HashMap,LinkedHashSet或者LinkedHashMap)最好先覆盖hashCode()和equals(),因为从Object继承下来的hashCode()方法默认是使用对象的地址计算散列值。



注意:

容器(Container)Spring 提供容器功能,容器可以管理对象的生命周期、对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称、如何产生(Prototype 方式或Singleton 方式)、哪个对象产生之后必须设定成为某个对象的属性等,在启动容器之后,所有的对象都可以直接取用,不用编写任何一行程序代码来产生对象,或是建立对象与对象之间的依赖关系。换个更直白点的说明方式:容器是一个Java 所编写的程序,原先必须自行编写程序以管理对象关系,现在容器都会自动帮您做好。常用容器:WebSphere,WebLogic,Resin,Tomcat


12.Java的泛型

JAVA中的泛型

泛型是一种数据类型参数化以最大限度的进行代码重用的技术。

泛型类的定义:
在类声明的同时通过<>声明泛型参数

<>中声明的类型参数可以用于定义类属性和类方法


泛型是一种模板

通过泛型可以创建任意个处理特殊类型的类

理论上可以把任何一个类都定义成泛型类。

实际开发中把容器类型的类定义成泛型类:链表  栈  队列

可以在定义泛型时候指定任意个的类型参数。

声明引用和创建对象时必须指定所有的类型参数

定义泛型类的构造方法时不需要增加泛型声明。


从泛型类派生子类

泛型类可以传入实际类型参数创建一个实现类,而且可以从泛型类派生子类;

当泛型类作为父类使用时不能再包含类型参数

使用泛型类是如果不指定类型实参,则类型实参默认为object

当泛型类作为父类使用时必须传入实际类型,否则所有类型参数被当做object类处理。


泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部份,那些部份在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。泛型的定义  泛型的定义主要有以下两种:   在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。在程序编码中一些包含参数的类。其参数可以代表类或对象等等。泛型的参数在真正使用泛型时都必须作出指明。

泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
泛型的好处Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。Java 程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“String 列表”或者“String 到 String 的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。


13.Java中的设计模式

Java 中的23 种设计模式: Factory(工厂模式), Builder(建造模式), Factory Method(工厂方法模式), Prototype(原始模型模式),Singleton(单例模式), Facade(门面模式), Adapter(适配器模式), Bridge(桥梁模式), Composite(合成模式), Decorator(装饰模式), Flyweight(享元模式), Proxy(代理模式),Command(命令模式), Interpreter(解释器模式), Visitor(访问者模式),Iterator(迭代子模式), Mediator(调停者模式), Memento(备忘录模式),Observer(观察者模式), State(状态模式), Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibleity(责任链模式)

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。

更详细http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html



基础很重要,任何时候都要牢记这点

最后,隆重推荐博客http://blog.csdn.net/xhuang861030/article/details/11839631


0 0
原创粉丝点击