本文共 6303 字,大约阅读时间需要 21 分钟。
BAT TMD这些大厂对员工的技术要求都比较高,但是一般面试都会从基础开始,基础面不通过后面的自然不行。而且基础知识都是可以深度挖掘的,遇到那种喜欢从基础知识挖掘的面试官,基础不行肯定面试不通过。所以我们继续为大家整理基础面试的知识点,作为抛砖引玉,希望您能有所收获。
维护、拓展性:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现这个类满足不了你了,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
安全、严密性:接口是实现软件松耦合的重要手段,它描叙了系统对外的所有服务,而不涉及任何具体的实现细节。这样就比较安全、严密一些(一般软件服务商考虑的比较多)。
其实接口的意义属于编程中的一种规范,具体的意义需要在编程中去感知,没有一个严格的定论。
<? extends T>
限定参数类型的上界:参数类型必须是T或T的子类型
<? super T>
限定参数类型的下界:参数类型必须是T或T的超类型,类型最高可到Object ,最低是T 泛型的中的通配符挺难理解的,我这里举个例子解释一下:
(1)首先我们定义几个类:class Fruit { }class Apple extends Fruit { }class Orange extends Fruit { }
下面这个例子中,我们创建了一个泛型类Reader,然后在fun1()中当我们尝试:
Fruit f = fruitReader.readExact(apples);
编译器会报错,因为List<Fruit>
与List<Apple>
之间并没有任何的关系。
public class GenericReading { static Listapples = Arrays.asList(new Apple()); static List fruit = Arrays.asList(new Fruit()); static class Reader { T readExact(List list) { return list.get(0); } } static void f1() { Reader fruitReader = new Reader (); // Errors: List cannot be applied to List . // Fruit f = fruitReader.readExact(apples); } public static void main(String[] args) { fun1(); }}
但是按照我们通常的思维习惯,Apple和Fruit之间肯定是存在联系,然而编译器却无法识别,那怎么在泛型代码中解决这个问题呢?我们可以通过使用通配符来解决这个问题:
static class CovariantReader{ T readCovariant(List list) { return list.get(0); }}static void f2() { CovariantReader fruitReader = new CovariantReader (); Fruit f = fruitReader.readCovariant(fruit); Fruit a = fruitReader.readCovariant(apples);}public static void main(String[] args) { f2();}
这样就相当与告诉编译器, fruitReader
的readCovariant
方法接受的参数只要是满足Fruit的子类就行(包括Fruit自身),这样子类和父类之间的关系也就关联上了。
这个知识点还包含 PECS原则:
上面我们看到了类似<? extends T>
的用法,利用它我们可以从list里面get元素,那么我们可不可以往list里面add元素呢?我们来尝试一下: public class GenericsAndCovariance { public static void main(String[] args) { // Wildcards allow covariance: List flist = new ArrayList(); // Compile Error: can't add any type of object: // flist.add(new Apple()) // flist.add(new Orange()) // flist.add(new Fruit()) // flist.add(new Object()) flist.add(null); // Legal but uninteresting // We Know that it returns at least Fruit: Fruit f = flist.get(0); }}
答案是否定,Java编译器不允许我们这样做,为什么呢?对于这个问题我们不妨从编译器的角度去考虑。因为List<? extends Fruit> flist
它自身可以有多种含义:
List flist = new ArrayList();List flist = new ArrayList ();List flist = new ArrayList ();
所以对于实现了<? extends T>
的集合类只能将它视为Producer向外提供(get)元素,而不能作为Consumer来对外获取(add)元素。
如果我们要add元素应该怎么做呢?可以使用<? super T>
:
public class GenericWriting { static Listapples = new ArrayList (); static List fruit = new ArrayList (); static void writeExact(List list, T item) { list.add(item); } static void f1() { writeExact(apples, new Apple()); writeExact(fruit, new Apple()); static void writeWithWildcard(List list, T item) { list.add(item) } static void f2() { writeWithWildcard(apples, new Apple()); writeWithWildcard(fruit, new Apple()); } public static void main(String[] args) { f1(); f2(); }}
这样我们可以往容器里面添加元素了,但是使用super的坏处是以后不能get容器里面的元素了,原因很简单,我们继续从编译器的角度考虑这个问题,对于List<? super Apple> list
,它可以有下面几种含义:
List list = new ArrayList();List list = new ArrayList ();List list = new ArrayList
当我们尝试通过list来get一个Apple的时候,可能会get得到一个Fruit,这个Fruit可以是Orange等其他类型的Fruit。
根据上面的例子,我们可以总结出一条规律,”Producer Extends, Consumer Super”:如何阅读过一些Java集合类的源码,可以发现通常我们会将两者结合起来一起用,比如像下面这样:
public class Collections { public staticvoid copy(List dest, List src) { for (int i=0; i
上面这个例子来自于网络,我觉得是目前最能说清楚泛型通配符差异的例子。感兴趣的可以去看看原文:
Java序列化的方式有:Java原生以流的方法进行的序列化、Json序列化、FastJson序列化、Protobuff序列化等。
Android可用的序列化方式还有Parcelable。 Serializable和Parcelable的区别:位于java.io的路径下,显然是Java通用接口。实际上Serializable就是把Java对象序列化为二进制文件,然后进行传递就非常方便了(这里的传递包括进程之间,程序与网络之间,以及程序与本地存储之间)。使用起来相当简单,比如你自己写一个Person类,只要该类implements了Serializable接口就可以了。好了,这时候你的Person对象就已经实现了序列化,可以通过Intent对象随便跳转了。虽然按照规矩此时应该奉上一段示例代码,但实在是太简单了就算了。
通过Serializable接口实现的序列化虽然方便,但却有个“致命”的问题,那就是效率不高,而在这方面有着洁癖的Google表示不能忍,就给我们提供了更高效率的Parcelable,反正大部分开发人员序列化都是为了传递对象而不是保存在本地,使用Parcelable确实更高效。
静态方法属于静态绑定,在编译阶段已经确定函数名和地址。重写的意思是重新定义父类的虚函数,但是虚函数是动态绑定的,而静态方法是静态绑定的,所以静态函数必然不能是虚函数,也就不存在所说的重写了。你在子类中重新写一个同名的函数,覆盖了父类的同名函数,在使用子类指针进行调用的时候,调用的就是子类的这个静态方法。
我们运营了一个公众号,为了跟大家一起分享学习的知识,感兴趣可以关注:码老板