接口和抽象类
所谓抽象类,其实是对一种事物的抽象。例如,给出两个动物,猫和狗,我们需要一个类来描述这两种动物,但是他们特性不同,所以可以将这个类设计为抽象类。而接口是对一种行为的抽象,例如,猫和狗都有叫声这个行为,但是具体实现又不同,所以将这个方法设计为接口,用到时再具体实现。
抽象类
1.抽象类和抽象方法都要用abstract修饰 2.抽象方法必须在抽象类中,抽象类中可以没有抽象方法 3.抽象类不能直接创造对象(实例化) 4.抽象类的子类如果重写所有抽象方法,那么就是一个具体的类。抽象类的子类不重写所有抽象方法,那么子类还是一个抽象类。
使用:实例类 extends 抽象类{}
接口
1.使用interface修饰 2.接口不能实例化对象 3.修饰抽象方法 public abstract 修饰常量 public static final 4.使用:class Test implements 接口1,接口2,接口3
异同
相同点 :
1.他俩都不能实例化对象
2.可以包含抽象方法
3.定义好的抽象方法要求子类强制重写
不同点:
1.接口中的属性只能定义成静态常量,抽象类当中的属性是最普通的属性
2.接口中只有public权限,抽象类当中可以包含四种权限
3.接口中的抽象方法不用abstract修饰,抽象类当中必须使用abstract
4.抽象类可以包含普通方法和抽象方法,但是接口只能包含抽象方法
5.接口不能定义构造函数,抽象类可以
6.接口可以进行多继承,多实现,抽象类只能单继承
7.定义的关键字和实现的关键字都不一样 接口(interface implements)
异常
定义:在程序运行过程中出现的错误,称为异常 。异常就是程序运行过程中出现了不正常现象导致程序的中断。 1.常见异常
- NullPointerException(空指针异常,要访问的变量没有引用任何对象时,抛出该异常)
- ArrayIndexOutOfBoundsException (下标越界异常)使用数组,尤其是做for循环判断条件的时候,很容易引发的一个问题。
- IndexOutOfBoundsExcention 索引越界异常,由于数组下标越界或字符串访问越界引起异常;
- SQLException 操作数据库异常
2.继承层次 如图,上图为Java中异常的体系结构,而Throwable类也继承于Object类。而后又分为Error和Exception两大类。异常又分为运行时异常和非运行时异常。 3.异常处理机制 关键字:
- try:用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch:用于捕获异常。catch用来捕获try语句块中发生的异常
- finally: finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
- throw:用于抛出异常
- throws:用在方法签名中,用于声明该方法可能抛出的异常。主方法上也可以使用throws抛出。如果在主方法上使用了throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现了异常,就交给JVM进行默认处理,则此时会导致程序中断执行。
try{可能发生异常的代码
}catch(关心异常具体类,不关心基类处理){
}finally{无论代码是否会发生异常,必定会被执行
System.exit(1);(这种情况除外)
}
Object类中的方法
- clone方法:保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常
- getClass方法:final方法,获得运行时类型
- toString方法:用的较多,一般子类对其进行重写
- finalize方法:用于释放资源,因为无法确定该方法什么时候被调用,很少使用。
- equals方法:该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法
- hashCode方法:该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。 - wait方法:wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。 (2)其他线程调用了该对象的notifyAll方法。 (3)其他线程调用了interrupt中断该线程。 (4)时间间隔到了。 此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。 - notify方法
该方法唤醒在该对象上等待的某个线程。 - notifyAll方法
该方法唤醒在该对象上等待的所有线程。
为什么要同时重写equals和hashcode方法? 以下是关于hashcode的一些规定: 两个对象相等,hashcode一定相等 两个对象不等,hashcode不一定不等 hashcode相等,两个对象不一定相等 hashcode不等,两个对象一定不等
假如只重写equals而不重写hashcode,那么Student类的hashcode方法就是Object默认的hashcode方法,由于默认的hashcode方法是根据对象的内存地址经哈希算法得来的,显然此时s1!=s2,故两者的hashcode不一定相等。 然而重写了equals,且s1.equals(s2)返回true,根据hashcode的规则,两个对象相等其哈希值一定相等,所以矛盾就产生了,因此重写equals一定要重写hashcode,而且从Student类重写后的hashcode方法中可以看出,重写后返回的新的哈希值与Student的两个属性有关。
类加载
类加载时机: 1.new 对象 2.类名.static成员 3.反射: Class.forName(“People”) 4.main函数所在的类优先被加载 class TestDemo{ public static void main(String[] args){
} } 5.先初始化父类 在初始化子类 类的初始化顺序 :静态变量→静态块→实例变量→实例块→构造函数。
子类的初始化顺序:父类 静态变量 父类静态块 子类 静态变量 子类静态块 父类的实例变量 实例块 构造 子类的实力变量 实例块 构造 类加载过程 双亲委派模型
反射
概述:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
一般情况下 拿到当前类的Class对象有下面四种方式 Class<?> c = People.class; People p = new People(); Class<?> c = p.getClass(); Class<?> c = Class.forName(“People”) 一个正常的类加载过程是这样的:
而反射通过Class对象 获取构造方法 生成当前类的对象
优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射; (2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
String类 jdk1.8
4.1 实现以及相关方法
String字符串对象本质是一个final修饰的字符数组对象。因为被final修饰,所以字符串是常量,它们的值一旦被创建后不能改变。同样String类也不能被继承。 常用方法 使用字符数组创建String对象
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
获取指定下标位置的元素
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
获取字符串长度
public int length() {
return value.length;
}
转化为字节数组
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
4.2 字符串拼接
”hello“ + ”world“ 和 s + ”world“的区别
+运算符如果一边是变量的话就会 先查找常量池中有没有拼接完的 如果有常量池就不会在新建 否则会新建一个常量 最后会在堆中创建一个新的String对象。也就是说s+”world“在拼接时原来的s对象并没有改变,只是s在拼接完成后会指向新创建的String对象引用。
如果二端都是常量 先查找常量池中有没有拼接完的 如果有常量池就不会在新建 否则会新建一个常量 但不会在堆中创建新的对象
4.3 字符串比较 “==”和“equals”
什么是==? == 等于比较运算符,如果进行比较的两个操作数都是数值类型,即使他们的数据类型不相同,只要他们的值相等,也都将返回true.如果两个操作数都是引用类型,那么只有当两个引用变量的类型具有父子关系时才可以比较,而且这两个引用必须指向同一个对象,才会返回true.(在这里我们可以理解成 ==比较的是两个变量的内存地址)
什么是equals? equals()方法是Object类的方法,在Object类中的equals()方法体内实际上返回的就是使用==进行比较的结果.但是我们知道所有的类都继承Object,而且Object中的equals()方法没有使用final关键字修饰,那么当我们使用equal()方法进行比较的时候,我们需要关注的就是这个类有没有重写Object中的equals()方法.
区别 == 是java提供的等于比较运算符,用来比较两个变量指向的内存地址是否相同.而equals()是Object提供的一个方法.Object中equals()方法的默认实现就是返回两个对象==的比较结果.但是equals()可以被重写,所以我们在具体使用的时候需要关注equals()方法有没有被重写. Object中的equals
public boolean equals(Object obj) {
return (this == obj);
}
String中的equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
可以看到String类是重写了equals的方法的
4.4 string、stringbuffer、stringbuilder的区别
首先,三者的共同之处:都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用。其次区别在于
- 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
- StringBuffer与StringBuilder两者共同之处:可以通过append、indert进行字符串的操作。
- 运行速度快慢为:StringBuilder > StringBuffer > String
总的来说: String:适用于少量的字符串操作的情况 StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况 StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
|