面向对象
- 继承:使用继承,能够复用父类的代码,而且维护和修改更容易。如果是通用方法,直接修改父类。如果是针对个别的方法则可以直接修改子类。
- 多态:对父类方法进行重写后,调用各个子类的同一名方法,表现出不同的状态,称为为多态。而且在使用父类的地方可以使用子类,所以有些方法可以不用关心传入的参数是属于哪个子类,直接调用方法即可,典型的就是回调方法。
- 抽象:抽象是指,把类的方法行为抽象出来,而不去关心具体的实现。在java中表象为接口和抽象类。接口和抽象类区别于普通类的点在于,抽象类和接口一般都不提供具体的实现,而是提供抽象方法和接口函数。当然实际上在java中抽象类可以编写具体实现方法 ,接口也可以使用default实现默认代码实现。
- 封装:封装是指屏蔽内部特性和行为而只提供一些操作方法,java中具体的实现就是使用default(不写,默认包访问权限),public(公开),private(私有,当前类可访问),protected(子类可访问)
反射
一般我们实例化一个类,都是在代码中硬编码完成的,但是通过反射,可以动态的完成类的实例化或者直接调用类的方法,最典型的应用就是spring的IoC了。
//可以通过字符串获得class对象
Class clazz = Class.forName("类的全路径");
//获取方法
Method[] method=clazz.getDeclaredMethods();
//根据方法名获取方法
Method m = clazz.getDeclaredMethod("getMsg");
//实例化对象
Object obj= clazz.newInstance();
//调用方法
m.invoke(obj);
泛型
- 泛型的类型擦除
可以通过创建泛型方法和泛型类来复用代码,另外一方面,泛型是类型安全的,即在编译时即可检查类型是否安全而不是在运行时检查,所以性能上会好一些。
不过区别C#的泛型,java的泛型使用了类型擦除,所谓类型擦除即在编译后,所以的泛型代码都被消除掉,而使用类型转换。所以被诟病为不是真正的泛型
List<String> list = new ArrayList<>();
list.add("1");
String a = list.get(0);
//编译后会被转换为以下代码,擦除掉List的类型信息,而在返回时使用强制类型转换
List list = new ArrayList<>();
list.add("1");
String a = (String)list.get(0);
- List和List<?>的区别
List<?>可以看作 是List的父类,因此,不会包含T的类型信息
List<Integer> l = new ArrayList<Integer>();
l.get(0).intValue();
List<?> l1 = new ArrayList<Integer>();
l1.get(0).intValue();//这一句会报编译错误,因为?所代表的类并不包含Integer的类型信息。
异常和错误
Throwable是异常(Exception)和错误(error)的超类
- Error
Error类是java运行时系统内部错误或者资源耗尽错误,应用不会抛出错误,如果发生错误,除了通知用户,只能尽力的使程序安全的终止,常见的错误有内存溢出错误,堆栈溢出错误。 - Exception
java的异常分为 RuntimeException 和 CheckedException
RuntimeException时运行时才会检测到的异常,如空指针异常,算数异常,类型转换异常。
CheckedException异常实际上也是只有运行时才会触发,但是java在编译时,强制要求对这些异常进行捕获,也就是必须把有可能发生这类异常的代码放在try{}代码块中,或者在方法标注 throws 指定异常。不过被标注的方法也是要求必须放在try代码块中。
public void test() throws Exception {
throw new Exception();
}
public void test() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
常见的CheckedException包括IO异常,SQL异常等。
- 异常的处理方式
可以通过try catch捕获异常,使用finally来执行一定会执行的后续代码。
其中值得注意的是,可以使用多个catch捕获不同的异常,但是顺序从上到下,如果有一个符合就会抛出异常。
public void test(){
try {
throw new Exception();
}
catch (NullPointerException e){
return;
}
catch (Exception e) {
e.printStackTrace();
}
finally {
//
}
}
如果在catch里面return了,finally还会执行吗?当然会,会把返回的数据保存(如果有返回数据),再执行finally,最后再return
- throw 和throws
throw 是用于代码直接抛出一个异常,执行到throw所在的代码,一定会抛出异常。
而throws用于方法,可跟多个异常,方法内任意行发生被标注的异常,才会被抛出到调用该方法的位置。
注解
注解实际上只是标记,当对类,方法或者属性等添加注解时,会在类加载阶段,根据注解,对类,方法,属性进行标注。
而实际实现功能的代码则是通过反射获取类的注解信息,然后做一些操作。
- 4种标准注解
- @Target:修饰对象的范围,说明注解用于包,type(类,接口,枚举,注解),类型成员(方法,构造函数,成员变量等)和方法参数以及本地变量
- @Retention:注解的运行范围
- SOURCE:源文件有效
- CLASS:类文件有效
- RUNTIME:运行时有效
- @Documented:用于javadoc显示文档
- @Inherited:标记该注解功能被继承,如果一个类使用包含该注解的注解,那么他的子类都会拥有该注解的功能
- 自定义注解,使用 @interface定义,可以使用注解修饰注解
@Target(ElementType.FIELD)
public @interface FruitProvider{}
序列化和反序列化
序列化是把实例信息转化为字符串或者其他可保存格式可转移的形式(可用于通讯等),使用JackJson等实际上就是序列化。不过java本身也提供了序列化的方法.
只要类实现 java.io.Serializable 接口即可被序列化,
但是我们会发现Serializable接口里说明都没有,所以更像是一个标记。使用transient关键字标注变量可以让其不被序列化。
public interface Serializable {}
public class person implements Serializable {
private int age;
private String name;
private transient String password;
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;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class SerializableDemo1 {
public static void main(String[] args) throws Exception, IOException {
//初始化对象
User1 user = new User1();
user.setName("yaomy");
user.setAge(23);
System.out.println(user);
//序列化对象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
oos.writeObject(user);
oos.close();
//反序列化
File file = new File("template");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User1 newUser = (User1)ois.readObject();
System.out.println(newUser.toString());
}
}
动态代理
代理最典型的应该是spring AOP功能,通过代理,可以拦截一些方法调用,然后做一些操作,如利用AOP实现的日志分离。
使用动态代理的关键是调用Proxy类的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){...}
第一个参数是类加载器:通过 obj.getClass().getClassLoader()获得
第二个参数是实现的接口:obj.getClass() .getInterfaces(),这也说明类必须实现接口才可以使用该方法进行代理,如果没有实现接口,只能使用CGlib进行字节码增强
第三个参数是处理逻辑,需要自己实现接口InvocationHandler,该接口只有一个方法,不过一般还要实现构造函数,把被代理的对象传入
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
动态代理例子
//普通的接口
interface Service {
public void add();
public void update();
}
//普通的接口实现类
public class ServiceImpl implements Service {
public void add() {
System.out.println("AService add>>>>>>>>>>>>>>>>>>");
}
public void update() {
System.out.println("AService update>>>>>>>>>>>>>>>");
}
}
//代理的处理逻辑
public class MyInvocationHandler implements InvocationHandler {
private Object target;
//构造函数,用于将被代理对象传入,以便调用原实例方法
MyInvocationHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("程序执行前加入逻辑");
// 执行原实例方法
Object result = method.invoke(target, args);
System.out.println("程序执行后加入逻辑");
return result;
}
}
public class Test {
public static void main(String[] args) {
Service aService = new ServiceImpl();
//创建代理处理类,并把被代理对象传入
MyInvocationHandler handler = new MyInvocationHandler(aService);
// 创建一个代理实例
Service aServiceProxy = (Service) Proxy.newProxyInstance(
aService .getClass().getClassLoader(), //类加载器
aService.getClass().getInterfaces(), //类实现的接口
handler//处理器
);
// 调用代理实例
aServiceProxy.add();
aServiceProxy.update();
}
}