1、Java中抽象类和接口,在开发中如何去选择?
- 抽象类的设计目的是代码复用,接口的目的是对行为进行约束
- 当is-A的关系,用抽象类。has-A的关系,用接口
- 比如我定义一个抽象类是Dog,可以有具体方法,sleep(),抽象方法eat()。那么新建一个握手的小狗ShakeHandDog,继承Dog。那么我就可以复用sleep的内容了,不用重新写了。eat方法我想交给子类定义,他们自己玩就好了。如果ShakehandDog是抽象就可以不实现抽象方法,否则必须实现。
- ShakeHandDog能够握手,握手这个功能,并不是狗天生就有的,所以我定义一个IHandshake的接口,有个doShakeHand方法,让ShakeHandDog实现这个接口,约束它必须有这个doShakeHand方法。
- 具体区别
- 抽象 abstract关键字、单继承。修饰符除了private都能有。可以有构造方法,具体方法抽象方法。子类是抽象类则可以不实现从抽象方法,某则必须申明方法的实现。目的是
重用 - 接口 inerface关键字、接口可以继承1个或多个接口。子类也能实现多个接口。修饰符默认public,只有这个。没有构造方法,没有具体方法。只有抽象方法。实现类要实现所有申明的方法。核心是
“约束,降低耦合”
- 抽象 abstract关键字、单继承。修饰符除了private都能有。可以有构造方法,具体方法抽象方法。子类是抽象类则可以不实现从抽象方法,某则必须申明方法的实现。目的是
2、重载和重写是什么意思,区别是什么?
- 重载是同一个类,可以存在多个同名但是参数内容或者顺序不一样的方法。
- 重写是重新写,父类的方法对子类不适用或者要增强时,子类可以对父类中继承的方法进行重写。
3、 什么是多态?
- 多态指的是,同一方法或者对象在不同情况下表现出不同的行为。
- 子类可以
重写父类的方法。那么同一方法会表现不同的行为。
class Animal {
public void makeSound() {
System.out.println("Some sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // output: "Bark"
animal2.makeSound(); // output: "Meow"
- 同一类,不同方法参不同而导致的
重载,那么也会导致同方法,不同行为。
4、静态内部类是什么?和非静态内部类的区别是什么?
- 在一类的内部再写一个类,就是非静态内部类,将这个类用static声明就是静态内部类
- 非静态内部类可以访问外部类的静态和非静态 成员。静态内部类只能访问外部类的静态成员
- 这个在反编译的时候可以看见,非静态内部类如果有引用了外部内,会直接持有外部类。
- 这个呢也是handler引起内存泄漏的原因。handler如果消息延迟,那么handleMessage()方法如果有用外部类的非静态方法或者非静态成员。必定会持有外部类。那么可达性分析来说handler.handleMessage()持有activity,Messagequeue的message持有handler(target时handler),messagequeue被looper持有,looper被threadlocal持有。threadlocal当前线程持有,那么根可达。所以activity是不会被释放低,所以造成内存泄漏。
- 解决方案1。静态内部类+弱引用。当activity被 ams持有的时候 ,弱引用是不会断的,当activity被ams放弃,那么一旦gc,弱引用就断了,activity就被回收了。
private static class MyHandler extends Handler{ private final WeakReference<MineActivity> mMineActivityWeak; public MyHandler(MineActivity mineActivity){ mMineActivityWeak = new WeakReference<>(mineActivity); } @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); MineActivity mineActivity = mMineActivityWeak.get(); if(mineActivity != null){ mineActivity.number = 5; } } } - activity ondestory时清除handler的message。handler.removeCallbackAndMessages(null)
4、java传参是值传递还是引用传递?
- 值传递:传递的参数是拷贝的值
- 引用传递:传递的是引用的地址
- 基本数据类型是值传递,拷贝一个值给执行的方法的参数,只是一个副本。
- 引用类型的参数,比如我新建一个object,id=10。会拷贝一个引用,objectParam给到方法的参数。如果方法内objectParam.id = 9,那么object.id会等于9。但是如果 objectParam = new Object()。那么object不受影响。因为引用的那个盒子里指向的堆中对象已经变了。
- 所以,都是值传递,没有引用传递。
5、使用equals和==进行比较的区别
- ==
- 基本数据类型比较的是值
- 引用数据类型比较的是地址
- equals
- 本质上还是在内部用==实现的
- 但有些方法会重写,比如String integer 。都写成了值比较。String是逐个字符串比较
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
}
}
- 当然上图其实描述不太准确,准确来说是,String str2 = new String("Hello");会在堆和方法常量池中各创建一个对象。String str = "Hello";只会创建一个对象,指向到方法去中的字符串常量池。一个是堆中的,一个是方法区常量池的,当然不一样。
6、String s = new String("xxx");创建几个对象? String s2 = "xxxxx";创建几个对象 ?
- String s = new String("xxx");首先堆中会创建一个String对象,如果方法区中的常量池中不包含"xxx",那么会在其中创建一个"xxx"对象,共创建2个对象。如果存在就不创建了,那就一个对象。最后栈上的引用s指向,堆中的String对象,再指向常量池中的“xxx”对象。
- 同理,如果"xxxxx"在常量池中存在就不会创建对象,不存在就创建一个"xxxxx"对象。
7、finally中的代码一定会执行吗?try里有return,finally还执行么?
- 一般情况下,会执行。try里有return,会吧return的值存在栈帧的局部变量表中,然后执行finally语句块,最后再从局部变量表中取回return的值返回。另外,如果try和finally中都有return,会用finally中的return,忽略try中的。
- 特殊情况下,
- finally之前那调用system.exit。或者再try中被kill。
- 在守护线程中,其它线程都执行完了,守护线程需要立刻停止,退出虚拟机,finally也有可能不执行
8、Java异常机制中,异常Exception与错误Error的区别。
- 两者都继承与Throwable
- Error是程序不能处理的错误,严重,比如OutOfMemmoryError,发生时,虚拟机一会中断线程。绝大多数程序运行是不被允许这种情况
- Exception,是程序可以处理的异常,分为运行时异常和非运行时异常
- 运行时异常,又称不受检查异常。java编译是不会告诉我有这个异常,运行时才会暴露出来
- 非运行时异常,,比如IOException、Sqlexception,必须处理,try catch或者throw。否则就不能编译通过
9、Parcelable,Serializable的区别
- serializable是Java提供的序列化机制,都能在Intent中传输
- 序列化就是将对象转为可存储或者传输形式的过程。序列化期间,对象被写入到临时或者永久性存储区,反序列化则是将存储中数据,创新创建为对象。
- Parcelable则是Android提供的适合于内存那种传输的序列化方式。
- 速度上Parcelable更快
- Serializable使用I/O读取存储在硬盘上。Parcelable直接内存中读写
- Serializable使用反射,序列化反序列化需要大量I/0。Parcelable自己实现封送/解封,不要反射,数据存在native内存中更快
- 使用上Parcelable更复杂
- Serializable只要实现Serializable接口
- Parcelable不仅仅是实现Parcelable接口,还需要实现一些方法。
- Android内存见传输数据用Parcelable,网络就用Serializable
10、为什么Intent传递对象要序列化?
- Intent传递数据本质上是使用Binder来完成的。Intent启动组件要借助AMS来完成,因此startActivity会离开当前进程到AMS所在的system_server进程来跨进程通信。那么对象就要在不同进程直接进行传输。
- 为了保护各个进程互相不干扰,进程隔离让,system_server进程无法直接获取应用进程中的对象。
- serializable是将对象序列化为二进制文件后进行传递。
- parcelable,本质上是将数据放到Parcel当中,如果是不同空间,举例,传递对象student然后把各个字段拆解拷贝到,parcel对象,再把parcel拷贝到内核空间,进程B从内核空间读取数据,得到parcel对象,根据其字段创建一个新的student。看起来就像是student对象从a到b。