目录
七、String、StringBuffer、StringBuilder
一、异常处理
Error
是程序无法处理的错误,表示的是代码运行时JVM出现的问题,如OOM。
Exception
是程序可以处理的异常,他的子类RuntimeExcption代表运行时异常,除运行时异常以外的异常都是非运行时异常
unchecked exception(非检查异常)
也称作运行时异常,编译器不要求必须进行异常处理,由程序员自己决定,如NullPointerException,IndexOutOfBoundsExcption。
checked exception(检查异常,编译异常)
也称作非运行时异常(运行时异常以外的异常就是非运行时异常),编译器强制程序员必须进行处理,否则编译不会通过,比如常见的IOException和SQLException。
二、comparable与comparator
comparable是内比较器,写在类的里面
comparator是外比较器,和比较的类分开写,耦合性较低。可以重写实现了comparable接口的类当中的compareTo方法。
主要以下两种情况使用comparator:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式(重写已经被重写的compareTo方法,覆盖掉实现comparable接口的compareTo方法)
1、实现Comparable:
package testJavaInterfaceAndMethod;
public class Person implements Comparable<Person>{//本类实现Comparable接口,内比较器方法只需要一个参数
private int age;
private String name;
public Person() {
}
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person o) {
return this.getAge()-o.getAge();
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
2、实现Comparator接口:
package testJavaInterfaceAndMethod;
importjava.util.Comparator;
public class PersonComparator implements Comparator<Person>{//外比较器,方法需要两个参数
//为什么不用重写Comparator中的所有抽象方法
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();//注意这里的顺序和实现Comparable接口的compareTo方法属性不一致。
}
}
测试:
package testJavaInterfaceAndMethod;
import java.util.Arrays;
import org.junit.Test;
public class Main {
@Test
public void testComparable() {
Person person1 = new Person(22,"dhl");
Person person2 = new Person(21,"pj");
Person[] persons = {person1,person2};
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);
}
Arrays.sort(persons,new PersonComparator());//使用外比较器的方法
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);
}
}
}
结果:
Person [age=22, name=dhl]
Person [age=21, name=pj]
Person [age=22, name=dhl]
Person [age=21, name=pj]
同时使用内比较器与外比较器,外比较器优先
三、Java编译时与运行时
编译时:将源代码编译为JVM可以识别的字节码
运行时:将字节码装载到内存中运行。
例:
public class ConstantFolding {
static final int number1 = 5;
static final int number2 = 6;
static int number3 = 5;
static int number4= 6;
public static void main(String[ ] args) {
int product1 = number1 * number2; //line A
int product2 = number3 * number4; //line B
}
}
解析:
lineA 是在编译期间计算的。
lineB 是在运行期间计算的。
常量折叠是种Java编译器使用的优化技术。由于final变量的值不会改变,因此就可以对它们优化。
方法重载:这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。
例:
public class {
public static void evaluate(String param1); // method #1
public static void evaluate(int param1); // method #2
}
方法覆盖:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。
例:
public class A {
public int compute(int input) { //method #3
return 3 * input;
}
}
public class B extends A {
@Override
public int compute(int input) { //method #4
return 4 * input;
}
}
子类B中的compute(..)方法重写了父类的compute(..)方法。如果编译器遇到下面的代码:
public int evaluate(A reference, int arg2) {
int result = reference.compute(arg2);
}
编译器是没法知道传入的参数reference的类型是A还是B。因此,只能够在运行时,根据赋给输入变量“reference”的对象的类型(例如,A或者B的实例)来决定调用方法#3还是方法#4
泛型(又称类型检验):这个是发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。
换句话来说,编译器会擦除所有在尖括号里的类型信息,来保证和版本1.4.0或者更早版本的JRE的兼容性。
例:
编译前:
List<Person> myList = new ArrayList(10);
编译后:
List myList = new ArrayList(10);
四、hashCode()与equals()方法
java的规定:如果两个对象 equals相等,那么hashCode一定相等(这里的equals和hashCode方法指的是没有重写的Object类中的方法, 未重写的equals()方法比较的是对象的地址(直接使用==比较)。
也就是说如果两个对象的地址相同,那么他们的hashCode一定相同,地址不同,那么就不一定了),但hashCode相等,对象不一定equals,hashCode不相等,那么对象一定不equals。
1.hashCode()方法存在的主要目的就是提高在散列结构存储中的查询效率。
如果没有hashCode方法的话,在HashMap中为了避免添加相同的key,那么有多少个元素就需要equals比较多少次,但是在如果有hachCode方法,就会先根据hashCode值找到对应的哈希桶,然后再进行equals比较,减少了比较的次数。
2.在set集合中判断两个对象相等的条件,其实无论是往set集合中存数据,还是从set集合中取数据,包括控制唯一性等,都是用这个条件判断的,条件如下:
首先判断两个对象的hashCode是否相等,如果不相等,就认为这两个对象不相等,就完成了。如果相等,才会判断两个对象的equals()是否相等,如果不相等,就认为这两个对象不相等,如果相等,那就认为这两个对象相等。
所以为保证set集合中的元素的唯一性:
1、在重写了equals方法后应该重写hashCode方法(不重写hashCode方法,equals但hashCode不同,此时就会重复)
2、而只重写hashCode方法而不重写equals方法:会产生hashCode不相同,但是对象可能equals,还是会出现元素重复。
五、finalize方法
finalize是在java.lang.Object类中定义,这个方法在gc启动,该对象被回收之前被调用。其实gc可以回收大部分的对象,所以一般是不需要程序员去实现finalize的。在对象回收的时候需要释放资源,此时可以实现finalize()方法。
一个对象的finalize()方法只能被调用一次,而且finalize()方法被调用不意味着gc会立刻回收该对象,所以有可能调用finalize()后,该对象又不会被回收,到了真正回收对象的时候,因为之前已经调用过了,所以不会调用finalize()方法,由此产生问题。另外,finalize()方法会忽略异常,即finalize代码中若出现异常,异常会被忽略。推荐不要主动使用finalize()方法,它跟析构函数不一样(超过变量的使用范围自动调用析构,调用析构函数后变量被销毁)。
六、面向对象、面向接口编程
万物皆对象,现实生活中每一个物体都属于一类事物,而每一个个体都是一类事物的实例。
面向对象有三大特性,封装、继承和多态。
1、封装就是将一类事物的属性和行为抽象成一个类,使其属性私有化,行为公开化,提高了数据的隐秘性的同时,使代码模块化。这样做使得代码的复用性更高。
2、继承则是进一步将一类事物共有的属性和行为抽象成一个父类,而每一个子类是一个特殊的父类--有父类的行为和属性,也有自己特有的行为和属性。这样做扩展了已存在的代码块,进一步提高了代码的复用性。
3、如果说封装和继承是为了使代码重用,那么多态则是为了实现接口重用。多态的一大作用就是为了解耦--为了解除父子类继承的耦合度。如果说继承中父子类的关系式IS-A的关系,那么接口和实现类之之间的关系式HAS-A。简单来说,多态就是允许父类引用(或接口)指向子类(或实现类)对象。很多的设计模式都是基于面向对象的多态性设计的。
总结一下,如果说封装和继承是面向对象的基础,那么多态则是面向对象最精髓的理论。掌握多态必先了解接口,只有充分理解接口才能更好的应用多态。
面向接口编程:接口也就是一种规范!面向接口编程就类似于现实中遵守公司规定一样!这样增强了系统的灵活性、可维护性,减小影响!实现项目中常说的:高内聚、低耦合!
七、String、StringBuffer、StringBuilder
String 字符串常量;StringBuffer 字符串变量(线程安全);StringBuilder 字符串变量(非线程安全)
String S1 = “This is only a” + “ simple” + “ test”;
其实就是:String S1 = “This is only a simple test”
对于字符串经常改变时,速度:StringBuilder>StringBuffer>String