期末考试部分复习内容
抽象类和接口
不同点:
1)接口只有定义,其方法不能再接口中实现,只有实现接口的类才能实现接口中定义的方法,而抽象类的方法可以再抽象类中被实现。
2)接口需要用implements实现,抽象类只能被继承(extends)
3)多个与单个的区别
4)接口中定义的成员变量默认修饰符为public static final(静态不能被修改),而且必须给其赋初值。
抽象类可以有自己的数据成员变量,也可以有非抽象的成员变量,而且抽象类中的成员变量默认为default(本包可见)。
抽象类中的方法前面有abstract修饰,不能用private、static、synchronize、native修饰,同时方法必须以分号结尾,不带花括号,如:
public abstract void print();
final与finalize与finallly
final ** 类似于const,类不可被继承,变量赋值完成就固定,方法**就不可被重写
finalize 在垃圾收集器将对象清理前执行finalize方法
是在Object类中定义的,因为所有类都可以调用他
**finally ** 用在try catch 语句块中
运行时异常和非运行时异常
运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过
非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
不使用构造函数构建对象
Java创建对象的几种方式(重要):
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
泛型容器重载过的toString
比如
public class Main
{
public static void main(String[] args) {
Set<String>s=new HashSet<String>();
s.add("3434");
s.add("34334");
s.add("343334");
System.out.println(s);
}
}
[34334, 3434, 343334]
注意它这个输出是有一对”[ ]“的,以及各个存储的内容 按 逗号+空格 分隔
说明类 对象及对象引用的概念 并举例
多态性包括哪些方面? 运行时多态性的三个必要条件
实现多态的三个必要条件
1.要有继承(实现implements)
2.要有重写(overWrite&overRide)
3.父类引用指向子类对象
main方法的定义前面的修饰符 和 参数的含义
public static void main(String[] args)
public
这个其实没什么可解释的,public可访问权限最高,如果要是private那就没得玩了。
static
static关键字是其中的重点。
首先来看static关键字本身。static关键字用来修饰成员变量或者方法。简单来说,static表示其修饰的成员变量或方法不需要实例化类就可以使用。
也就是说,static变量不依赖于类的任何实例,只要类被加载(加载不等于实例化),jvm就可以直接找到他们而不需要创建关于类的任何对象。
static关键字前可以有修饰符进行修饰,当使用public static时,其实相当于创建了一个全局变量(方法)。
再来看static修饰的代码块(即为static {…}形式的),也叫做静态代码块。概念不必多讲,静态代码块有一个非常非常非常重要的特点:随着类的加载而执行且只执行一次。
静态代码块可以有多个,位置可以随便放,它不在任何的方法体内,jvm加载类时会优先执行这些静态的代码块,如果static代码块有多个,jvm将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
这样,public static两组关键字的意义就显而易见了。
void
与C系语言不同的是,java语言默认都是正常状态结束。也就是System.exit(0),异常结束是1。所以,java的主方法自然没有必要存在返回值,也就是void。
main
作为编程语言界的晚辈,自然不能另立门户,沿袭C系列main命名的主函数也是情理之中的事。
String[]
为什么主函数还有形参?从来没见过啊?
其实都是IDE用多了,使用java命令运行class文件的时候是可以附加参数的,比如 java HelloWorld test 100 这种写法。test及100都会作为参数传入mian方法。但因为参数并不仅限一个,所以定义成数组格式好了。
为什么是String数组?
String作为java中最万能的包装类具有普遍性。天地万物都可以解释成String,String也可以解析成天地万物,所以用String来存放参数自然是最优选择。
ps.在java 1.5以后,String[]完全可以写成String…(不定项参数),不妨试一下。
args
计算机领域用来表示参数的变量名其实就那么几个,params,args算是约定俗成的了。其实完全可以叫abc,开心就好
类的第四个成员------程序块(代码块)
作用:代码块跟普通方法一样,是用来做事情的
写法:可以认为程序块是一个 没有参数 没有名字 没有返回值的特殊方法,就单一对大括号,把内容都包起来
{;;;;;;}
用法:块也需要调用才能执行 因为没有名字什么都没有,所以我们自己无法调用。每一次我们调用构造方法创建对象之前,系统会帮我们自动的调用一次程序块,让它执行一遍。存在多个代码块的时候,挨个从上往下执行
特点:没有什么重载的概念(因为它没有名字 没有参数)
块可以在里面写一些程序,我想要在创建对象之前执行
static代码块 (就是在代码块基础上对多个对象也总共执行一次)
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们。
他是类加载器要做的活,一定优先把各种类的static代码块给加载一遍
每个代码块只会被执行一次。
例如:
public class Test5 {
private static int a;
private int b;
static{
Test5.a=3;
System.out.println(a);
Test5 t=new Test5();
t.f();
t.b=1000;
System.out.println(t.b);
}
static{
Test5.a=4;
System.out.println(a);
}
public static void main(String[] args) {
// TODO 自动生成方法存根
}
static{
Test5.a=5;
System.out.println(a);
}
public void f(){
System.out.println("hhahhahah");
}
}
运行结果:
3
hhahhahah
1000
4
5
注意构建对象的时候调用顺序
main方法 静态代码块(static) 非静态代码块 构造方法 run方法
父类静态代码块——>子类静态代码块——>父类代码块——>父类构造方法——>子类代码块——>子类构造方法
String[] args
Java中主函数常见的形式是:public static void main(String[] args) {}
c中 char*[] argv argv[0]是程序的名称,并且包含了程序所在的完整路径
java不同于他,args[0]不是程序的名称,他直接保存了命令行参数的第一个字符串
类的多态
•如果没有显式地指明父类,则从Java的标准根类Object进行继承(默认extends Object)
核心要理解到:父类它实际上copy了它的所有成员和方法给子类
子类中有三种方法:
1)子类重写某个方法,就是在将某个函数进行更新,对于该子类而言,访问该方法的优先级更高
2)子类没有重写父类的方法,那么它就是直接继承了父类的方法,同样可以进行访问
3)子类新的方法,那么通过父类的引用,不能直接访问子类新的方法,除非你进行 强制类型转化(向下转型)
覆盖
覆盖则指的是父类引用指向了子类对象,调用的时候会调用子类的具体方法。
方法覆盖的条件
①必须是继承关系
②子类的方法必须与父类的方法名字,参数的数目和类型必须一致
static修饰的方法不能覆盖,static修饰的方法不能继承,既然不能继承当然也就不能覆盖了。还有一种说法,“因为静态方法是绑定类,而实例方法绑定对象。 静态属于类区域,实例属于堆区域”,也就是main方法不能覆盖。
向上转型
将子类对象的引用转换成父类对象的引用,称为向上转型
采用动态绑定(dynamic binding)技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
声明的是父类的引用,但是执行的过程中调用的是子类的对象,程序首先寻找子类对象的method方法,但是没有找到,于是向上转型去父类寻找
如果子类重写了父类的method方法,根据上面的理论知道会去调用子类的method方法去执行,因为子类对象有method方法而没有向上转型去寻找
——子类引用不能指向父类对象(本来父类指向子类可以的是因为子类本来就有一块是父类属性)
——向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的。(这就需要下面的向下转型来处理了)
以及很重要的一点,在参数位置,也可以进行向上转型
向下转型(强制转换)
将父类类型的引用变量强制(显式)地转换为子类类型
当子类某些方法没有出现在父类中,而我们要通过父类的引用去访问子类,就要进行强制类型转换
抛出异常部分
Exception是要继承extends,而非implements
抛出异常的基本模块
try { }
catch(Exception1 o){ } //建立一个异常类的引用,去指向try中产生的异常对象
catch(Exception2 o){ } ... //可接受多种异常类型
finally //这里不是final,一般可以用来进行最终清理
finally:
被finally控制的语句体一定会执行
特殊情况:在执行到finally之前JVM退出了(比如System.exit(0))
throw与throws:
1)使用的位置不同:throw是写在方法体里面。throws则是用在方法头上面
2)throw就是直接抛出一个异常,而throws则是说我这个方法可能会抛出一个异常。
——如果你的方法体里面加了throw,说明这个函数存在抛出异常的可能,那么你的方法头一定要加throws
并且 你throw的内容是实例化的对象(就是一定要new出来)
在自定义的异常类中
要注意 异常类构造函数的情况
例如 只有接收单个字符串的构造函数,也就是说,你new一个异常类的实例化对象时一定要给他传一个字符串,如果要为空,则要传null,
如:
ValueException ve=new ValueException(null);
常见异常类型分析
NullPointerException——异常空指针异常
在 C++中,声明的指针需要指向一个实例(通过 new 方法构造),这个指针可以理解为 地址。在 Java 中,虽然没有指针,但是有引用(通常称为对象引用,一般直接说对象),引 用也是要指向一个实例对象(通过 new 方法构造)的,从这种意义上说,Java 中的引用与 C++中的指针没有本质的区别,不同的是,处于安全的目的,在 Java 中不能对引用进行操作,而在 C++中可以直接进行指针的运算,例如 book++等。
所以这里的 NullPointerException 虽然不是真正的空指针异常,但本质上差不多,是因为引用没有指向具体的实例,所以当访问这个引用的方法的时候就会产生这种异常。
String str = "这是测试用的字符串!";
System.out.println(str.length());
String str1 = null;
System.out.println(str1.length());
//产生 NullPointerException异常
ClassCastException——类型转换错误
通常是进行强制类型转换时候出的错误
遇到这个错误可以有两种处理方式,(假设对象为 o)
1.通过o.getClass().getName()得到具体的类型,然后根据类型进行进行具体的处理。
2.通过if(o.instanceof 类型)的语句来判断o的类型是什么
ArrayIndexOutOfBoundsException——数组越界
UnsupportedClassVersionError——版本不支持
编译 Java 和运行 Java 所使用的 Java 的版本不一致。例如,编译的时候使用的 Java 版 本是 6,运行时候使用的 Java 版本是 5
NumberFormatException异常——数字转换异常
数字转换异常,在把一个表示数字的字符串转换成数字类型的时候可能会报这个异常,原因是作为参数的字符串不是由数字组成的
例如:Integer.toString()
其他常见的异常类
异常 | 说明 |
---|---|
RuntimeException | Java.lang 包中多数异常的基类 |
ArithmeticException | 算术错误,如除以 0 |
IllegalArgumentException | 方法收到非法参数 |
SecurityException | 试图违反安全性 |
ClassNotFoundException | 不能加载请求的类 |
AWTException | AWT 中的异常 |
IOException | I/O 异常的根类 |
FileNotFoundException | 不能找到文件 |
EOFException | 文件结束 |
IllegalAccessException | 对类的访问被拒绝 |
NoSuchMethodException | 请求的方法不存在 |
InterruptedException | 线程中断 |
小例题
如果父类Animal中有一个方法eat(),子类有Cat,Dog...
你开引用数组的时候是开Animal这个父类数组的,然后具体子类对象再具体new 对象
这样的好处就是 利用了向上转型的机制(动态绑定),在去调用子类重写(override)的方法,
那么假设说 你要喂所有的动物
你直接 这样就可以利用动态绑定 进行自动的对象切换
Animal[] animals;
...
for(Animal k:animals){
k.eat();
}
instanceof
用在向下转型中
比如我现在只有父类的引用,它可以指向子类中父类的成分,但是它不允许直接访问子类中非父类的成分,所以要让它也能访问子类中非父类的成分,就要加入强制类型转换,而转换成什么,可以用instanceof对它所指向的内容进行判断
instanceof前者是对象,后者是某个类
String s = "123";
if(s instanceof String){
System.out.println("s的类型为String类型");
}
instanceof比较的是对象,不能比较基本类型,否则会报错
Integer a=2;
if(a instanceof Integer){
System.out.println("...");
}
线程部分
线程是Thread类及其子类的实例
public Thread(Runnable target);
创建Thread对象要给他一个有run方法的对象,该对象要实现了run方法
public class X extends Thread ... 与public class X implements Runnable等价
就是说,只要run方法可以
1)连接Runnable接口,实现run方法
2)继承Thread类,重写run方法,再将之作为参数传给runnable target
3)继承Thread类,重写run方法,直接将该对象进行实例化,它就是线程了
start():该方法的调用把线程的虚拟CPU置为可运行(Runnable)状态
关于sleep()
它是Thread的方法,用于挂起该进程多久,传参毫秒
一般你如果extends Thread 就可以进行 直接的使用 sleep()方法
否则你可以直接使用 Thread.sleep(多少)(它是一个static方法)
其实它在main函数里也可以这样使用,我们就可以把main看作是一个进程,让他也sleep()
关于join()
在当前线程中执行t.join()方法使当前线程等待,直到线程 t 结束为止,再让原线程恢复到runnable状态
就是表示一旦某个线程调用了join方法,那么就要一直 运行到该线程运行结束,才会运行其他进程
关于synchronized
为了防止多线程同时在进行某个代码片段时 对共享数据产生了影响
某个方法加上synchronized修饰后,该方法同一时间只能让某一个线程独享
如果只是某一段代码片段,可以写成内嵌型的,如:
synchronized(this){
...
}
对象锁具有可重入性:允许已拥有某对象锁的线程再次请求并获得该对象锁