单继承多实现
理解: 如果一个类继承了类 A 和类 B,A 和 B 都有一个 C 方法,那么当我们用这个子类对象调用 C 方法的时候,jvm 就晕了,因为他不能确定你到底是调用 A 类的 C 方法还是调用了 B 类的 C 方法。而 多实现就不会出现这样的问题,假设 A 和 B 都是接口,都有 C 方法,那么问题就能解决了,因为接口 里的方法仅仅是个方法的声明,并没有实现,子类实现了 A 和 B 接口只需要实现一个 C 方法就 OK 了, 这样调用子类的 C 方法时,Java 不至于神志不清。从另外一个方面考虑的话应该就是 Java 是严格的面 向对象思想的语言,一个孩子只能有一个亲爸爸。
线程是CPU调度的最小单元,同时线程是一种有限的系统资源进程一般指一个执行单元,在PC和移动设备上一个程序或者一个应用一般来说,一个App程序至少有一个进程,一个进程至少有一个线程(在内存允许的情况下已开启N个进程,在A ndroidMenifest 中给四大组件指定属性android:process开启多进程模式),
创建:继承Thread类或者直接实现 Runnable 接口来重写 run()方法实现线程
常用方法:
1 start() 用于启动线程
2 run() 调用线程对象中的run方法
3 join() 合并插队到当前线程
4 sellp() 睡眠释放cpu资源
5 setPriority() 设置线程优先级
1 wait() 是object 方法,sleep()是Thread类的方法
2 wait() 使线程进入等待状态,并且释放锁,使得其他线程能使用同步控制块或者方法,sleep()是使线程进入睡眠状态,不释放锁,在睡眠时间用完后使用interrupt()方法中断,线程唤醒进入就绪状态
注意:sleep()方法只会让出CPU,并不会让出同步资源锁,wait()必须持有对象锁,而sleep()可以用在任意地方
1 stop()方法(废弃):因为线程不安全,释放所有锁不可控,线程终止不应该由其他线程控制 ,而是应该由线程自己控制
2 退出标致法:需要while()循环在某些特定条件下退出,最直接的办法就是设置一个boolean 标志,并通过设置这个标志来控制循环是否退出
public void stop(View view) {
FiveThread runnable = new FiveThread();
Thread fiveThread = new Thread(runnable);
fiveThread.start();
SystemClock.sleep(1000);
runnable.cancel();
}
public class FiveThread implements Runnable {
private volatile boolean isCancelled;
public void run() {
Log.e("TAG", "FiveThread:"Thread.currentThread().getName());
while (!isCancelled) {
Log.e("TAG", "线程没有中断");
SystemClock.sleep(500);
}
Log.e("TAG", "线程FiveThread终止了");
}
public void cancel() {
sCancelled = true;
}
}
synchronized用于多线程访问,被synchronized修饰的部分不能同时被执行,是代码同步的一种方式。synchronized 加到 static 方法前面是给class 加锁,即类锁,类锁对该类的所有对象都起作用,对象锁能;synchronized 加到非静态方法上是给对象上锁,即对象锁。
使用synchronized修饰方法时,当多个线程同时访问被synchronized修饰的方法时,有且仅有一个线程可以访问该方法,当一个线程在访问时,其它线程只能等待。当一个线程访问完毕后,下一个线程才可以访问。
当方法被synchronized修饰后,如果想要执行该方法就必须获得相应的锁。每个类有且仅有一个锁(针对静态方法),每个类的实例也是有且仅有一个锁。当多个线程在同时访问同一个方法时,执行该方法就必须获得相应的锁,同时锁只有一个,所以只能有一个线程可以获得锁,其它的线程必须等待该线程释放锁后才能获取到该锁。
Volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。
volatile不如synchronized安全,在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更难以理解。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它。一般来说,用同步机制会更安全些。
Synchronized 跟 Volatile 的区别:
1 volatile 仅能使用在变量级别; synchronized 则可以使用在变量、方法、和类级别的
2 volatile 仅能实现变量的修改可见性,并不能保证原子性; synchronized 则可以保证变量的修改可见性和原子性
3 volatile 不会造成线程的阻塞; synchronized 可能会造成线程的阻塞。
4 volatile 标记的变量不会被编译器优化
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
理解:如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。
预防:
1 以确定的顺序获得锁
2 超时放弃,当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。
1 synchronized 是一个关键字而lock是一个接口
2 synchronized 是隐式的加锁,lock是显示的加锁。
3 synchronized 可以作用在方法和代码块上而lock只能作用在代码块上。
4 synchronized 没有超时机制,而lock中的trylcok可以支持超时机制。
5 synchronized 不可中断,而lock中的lockInterruptibly可中断的获取锁。
6 synchronized 只有一个同步队列和一个等待队列,而lock有一个同步队列,可以有多 等待队列
7 synchronized 是非公平锁,而lock可以是公平锁也可以是非公平锁。
8 synchronized 用object的notify方法进行唤醒,而lock用condition进行唤醒。
简述:
Handler将Message发送到Looper的消息队列中,即MessageQueue,等待Looper循环读取Message,处理Message,然后调用Message的target,即附属Handler的dispatchMessage方法,将该消息回调到handleMessage方法中,然后完成UI更新。
问:Looper是如何工作的?
首先Looper对象会保存一个与之关联的线程的引用,Looper是通过ThreadLocal来获取到当前线程的looper对象的(ThreadLocal类的特性)ThreadLocal是线程内部的数据存储类,当使用ThreadLocal维护变量的时候,它会为每个使用该变量的线程提供一个独立的变量副本,这个变量副本是该线程独有,不受其他线程影响。因此当Looper的prepare()中调用ThreadLocal.get()时,就获取到了当前线程的一个独有的Looper对象,即在在当前线程关联了一个Looper对象,Looper的实现正是巧妙利用了Threadlocal的特性来实现的,避免了去使用哈希表来维护不同线程及其对应Looper的映射关系。
问:一个线程会有几个Looper,几个Handler,以及Looper会存在线程的哪里
一个线程一个Looper,可以有多个Handler,Looper会存在线程的ThreadLocal对象里,该对象是线程的缓存区
问:Handler中loop方法为什么不会导致线程卡死
初始化后,所有主线程做的事情都是在looper.loop()中完成的,因为主线程不做其他事,所以不会卡死
HandlerThread本身也是Thread,只是在Thread基础上封装上了Handler的载体,并且在run方法中创建了looper对象,这也是为什么在IntentService中能在HandlerThread中直接用handler的原因。而我们知道一个线程是可以有多个handler,所以用HandlerThread更加方便我们不用关心Handler的创建,一般用在多线程中直接处理任务
定义在一个类里边的类叫做内部类,对应的包含内部类的类叫做外部类。内部类不允许同一包中的其他类进行访问,但是内部类可以访问外部类的所有属性、方法,包括私有数据。
内部类有四种:匿名、成员、静态、方法(局部)内部类。
静态属性,静态方法可以被继承,不能被重写,而是被隐藏。当子类当中定义了静态的属性和方法,则父类的静态属性,方法称之为隐藏
问:静态内部类与非静态内部类的区别
按照是否有 static修饰分为静态内部类和非静态内部类,有static修饰的为静态内部类,没有static修饰的为非静态内部类
强引用:是指创建一个对象并把这个对象赋给一个引用变量。这种类型最为常见, 取消强引用,可以将相应的引用设置为null,就可以进行垃圾回收了
String strong = new String("Strong Reference")
软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果 内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它, 该对象就可以被程序使用。
软引用的作用:
软引用是用来描述一些有用但并不是必需的对象,可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。软引用在实际中有重要的应用,例如浏览器的后退按钮,这个后退时显示的网页内容可以重新进行请求或者从缓存中取出:如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建,如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出这时候就可以使用软引用
Object obj = new Object();
SoftReference
弱引用:弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足都会回收被弱引用关联的对象。弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型。在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。
Object obj = new Object();
WeakReference
虚引用:虚引用和前面的软引用、弱引用不同它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联 则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用 主要用来跟踪对象被垃圾回收的活动。虚引用必须和引用队列关联使用,当 垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引 用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个 虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之 前采取必要的行动。
Object obj = new Object();
ReferenceQueue
问:假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,该如何解决
用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。
1 final修饰,类,成员变量,成员方法,final修饰的类不可被继承,成员变量不可被修改,成员方法不可被重写。
final修饰的方法不能被重载
使用final方法的原因有两个。第一个原因是把方法锁定,以预防任何继承类修改它的意义。这是出于设计的考虑:你想要确保在继承中方法的行为不变,并且不会被重写。注意类中的private方法隐含为final方法,它不可被重写。因此,在private方法前加上final修饰符是没有意义的。
final修饰的类不能被继承
如果某个类整体被定义为final,则该类不可被继承,它不可有子类。这种设计可能是出于安全或其他方面的考虑。
2 finally和try…catch共同使用,用来使正常、异常情况最终都能得到处理。
3 finalize 是类方法,垃圾回收之前会调用此方法,重写此方法可以实现资源回收。
1 接口里只能包含抽象方法,静态方法和默认方法,不能为方法提供实现,也就是方法不能书写方法体,而抽象类中的普通方法可以为方法提供实现
2 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是被public static final 修饰的常量,常量必须赋值
3 接口中不能包含构造器,而抽象类中可以包含构造器,抽象类中的构造器并不是用于创建对象,而是让其子类调用构造器来完成属于抽象类的初始化操作
4 接口里不能包含初始化代码块,但抽象类里完全可以包含初始化代码块
5 一个类只能继承一个抽象类,而一个类却可以实现多个接口
内部类是定义在另一个类中的类,分为匿名,成员,静态,局部内部类,其都能独立的继承自一个接口的实现,无论外部类是否继承了一个接口的实现,对于内部类都没有影响。Java可以实现多个接口,但是只能单继承,因此内部类可以继承自多个具体或者抽象的类,来解决接口难以解决的问题,接口只是解决了部分问题,而内部类使得多继承的解决方案更加完整。
问:为什么需要内部类?
1 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
2 内部类可以对同一个包中的其他类隐藏起来。
3 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
成员内部类: 存在于某个类的内部,与全局属性或者方法同级的内部类就是成员内部类。
1(重要)成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括静态成员和私有成员)。
2 成员内部类和外部类的属性和方法名同名时,外部类的属性和方法会隐藏;但可以通过外部类.this.成员变量的方式访问外部类的属性和方法。
3 成员内部类和外部类的属性和方法名同名时,外部类的属性和方法会隐藏;但可以通过外部类.this.成员变量的方式访问外部类的属性和方法。
4 成员内部类对象会隐式的引用一个外部类对象。(可以解释第一点)
5 成员内部类对象会隐式的引用一个外部类对象。(可以解释第一点)
局部内部类:是定义在一个方法或者一个作用域里面的类。它与成员内部类的区别在于局部内部类的访问仅在于方法内或者作用域内。
1 不能有private、public、protected和static等修饰符,与局部变量类似。
2 只能在定义局部内部类的方法或者作用域中实例化;
3 局部内部类的对象不能使用该内部类所在方法或者作用域的非final局部变量
匿名内部类:不定义类的名字,在使用的地方直接定义对象。
1 唯一一种没有构造器的类;匿名内部类在编译时,编译器会自动起名xxx$1.class;
2 匿名内部类不能存在任何静态的变量、方法等;
3 匿名内部类是局部内部类的特例;
4 大部分匿名内部类用于接口返回;
静态内部类:在成员内部类的基础上加上一个static关键字就是静态内部类。
1 不需要依赖外部类。
2 不能使用外部类的非静态属性和方法。
泛型是 jdk5.0 版本出来的新特性,他的引入主要有两个好处,一是提高了数据类型的安 全性,可以将运行时异常提高到编译时期,比如 ArrayList 类就是一个支持泛型的类,这样我们给 ArrayList 声明成什么泛型,那么他只能添加什么类型的数据。第二,也是我个人认为意义远远大于第 一个的就是他实现了我们代码的抽取,大大简化了代码的抽取,提高了开发效率。比如我们对数据的操 作,如果我们有 Person、Department、Device 三个实体,每个实体都对应数据库中的一张表,每个 实体都有增删改查方法,这些方法基本都是通用的,因此我们可以抽取出一个 BaseDao,里面提 供 CRUD 方法,这样我们操作谁只需要将我之前提到的三个类作为泛型值传递进去就 OK 了。而数据的安全性,其实程序员本身通过主观意识是完全可以避免的,何况某些情况下,我们还真的想在 ArrayList 中既添加 String 类型的数据又添加 Integer 类型的数据。
为什么要用泛型:
1 拥有不同参数类型却有相同的执行流程的方法,需要使用泛型
2 指定数据类型,可以在编译期间发现类型错误,也不需要进行强制类型转换
使用Java的泛型时应注意以下几点:
1 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2 同一种泛型可以对应多个版本,不同版本的泛型类实例是不兼容的。
3 泛型的类型参数可以有多个。
4 泛型的参数类型可以使用extends语句例如习惯上称为“有界类型”。
5 泛型的参数类型还可以是通配符类型。
例如Class> classType = Class.forName("java.lang.String");
T泛型跟通配符泛型
? 表示不确定的java类型。
T 表示java类型。
K V 分别代表java键值中的Key Value。
E 代表Element。
泛型擦除:
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。
限定通配符:
一种是 extends T>它通过确保类型必须是T的子类来设定类型的上界, 另一种是 super T>它通过确保类型必须是T的父类来设定类型的下界。 另一方面表 示了非限定通配符,因为可以用任意类型来替代。 例如List extends Number>可以接受List或List。
问 :你可以把List 传递给一个接受List参数的方法么?
答: 不可以因为List可以存储任何类型的对象包括String, Integer等等,而List却只能用来存储Strings。
问: Array 中可以使用泛型么?
答:Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。
1 HashMap HashSet HashTable都是集合,底层都是Hash算法实现的
2 HashMap是Hashtable的替代品,这两个都是双列集合,而HashSet是单列集合
3 HashMap线程不安全、效率高、可以存储null键和null值;
4 Hashtable线程安全,效率低,不可以存储null键和null值, 因为它的remove,put,get做成了同步方法,保证了Hashtable的线程安全性。每个操作数据的方法都进行同步控制之后,由此带来的问题任何一个时刻只能有一个线程可以操纵Hashtable,所以其效率比较低。
5 HashMap实现了Map接口,存储键值对,通过put方法存储数据,通过key计算hashcode,取对象快,因为使用唯一的键获取对象。
6 HashSet实现了Set接口,存储对象,使用add方法添加元素,使用成员对象计算hashcode,在速度上没有HashMap快。
扩展:hashCode()方法的作用是计算对象的hash值,该方法会返回一个int值。其实是一串数字(对象的内存地址),就是指针了。。 主要用来比对俩个对象是不是同一个。判断两个对象(内容)是否相同,最先比较是否同一对象(直接用==),再比较hashCode,再调用equals方法
HashMap中元素是没有顺序的;TreeMap中所有元素都是有某一固定顺序的。HashMap继承AbstractMap类,是基于hash表实现的;TreeMap继承SortedMap类,是基于红黑树实现的。
1、HashMap是通过hashcode()对其内容进行快速查找的;HashMap中的元素是没有顺序的;
TreeMap中所有的元素都是有某一固定顺序的,如果需要得到一个有序的结果,就应该使用TreeMap;
2、HashMap和TreeMap都不是线程安全的;
3、HashMap继承AbstractMap类;覆盖了hashcode() 和equals() 方法,以确保两个相等的映射返回相同的哈希值;
TreeMap继承SortedMap类;他保持键的有序顺序;
4、HashMap:基于hash表实现的;使用HashMap要求添加的键类明确定义了hashcode() 和equals() (可以重写该方法);为了优化HashMap的空间使用,可以调优初始容量和负载因子;
TreeMap:基于红黑树实现的;TreeMap就没有调优选项,因为红黑树总是处于平衡的状态;
5、HashMap:适用于Map插入,删除,定位元素;
1 都是线程安全的
2 ArrayList和LinkedList都实现了List接口
3 ArrayList是动态数组的数据结构
4 LinkedList是双链表的数据结构,
5 在随机访问时候,ArrayList速度比较快,因为LInkedList是采用移动指针查找数据,指针从前往后查找浪费时间。
6 在进行删除插入时候LinkedList速度比较快,因为ArrayList会对操作点之后的所有数据进行下标索引的修改,并移动数据
并发集合常见的有 ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque 等。并发集位于 java.util.concurrent 包下,是jdk1.5之后才有的,在 java 中有普通集合、同步(线程安全)的集合、并发集合。普通集合通常性能最高,但是不保证多线程的安全 性和并发的可靠性。线程安全集合仅仅是给集合添加了 synchronized 同步锁,严重牺牲了性能,而且对并发的效率 就更低了,并发集合则通过复杂的策略不仅保证了多线程的安全又提高的并发时的效率。
备注:ConcurrentHashMap 是线程安全的 HashMap 的实现,默认构造同样有 initialCapacity 和 loadFactor 属性, 不过还多了一个 concurrencyLevel 属性,三属性默认值分别为 16、0.75 及 16。其内部使用锁分段技术,维持这锁 Segment 的数组,在 Segment 数组中又存放着 Entity[]数组,内部 hash 算法将数据较均匀分布在不同锁中。
put 操作:并没有在此方法上加上 synchronized,首先对 key.hashcode 进行 hash 操作,得到 key 的 hash 值,hash 操作的算法和map也不同,根据此hash值计算获取其对应的数组中的Segment对象,接着调用此 Segment 对象的 put 方法来完成当前
get 操作:首先对 key.hashCode 进行 hash 操作,基于其值找到对应的 Segment 对象,调用其 get 方法完成当前操作。 而 Segment 的 get 操作首先通过 hash 值和对象数组大小减 1 的值进行按位与操作来获取数组上对应位置的 HashEntry。在这个步骤中,可能会因为对象数组大小的改变,以及数组上对应位置的 HashEntry 产生不一致性
问答:ConcurrentHashMap 是如何保证的?
对象数组大小的改变只有在 put 操作时有可能发生,由于 HashEntry 对象数组对应的变量是 volatile 类型的, 因此可以保证如 HashEntry 对象数组大小发生改变,读操作可看到最新的对象数组大小。 在获取到了 HashEntry 对象后,怎么能保证它及其 next 属性构成的链表上的对象不会改变呢?这点 ConcurrentHashMap 采用了一个简单的方式,即 HashEntry 对象中的 hash、key、next 属性都是 final 的,这也就 意味着没办法插入一个 HashEntry 对象到基于 next 属性构成的链表中间或末尾。这样就可以保证当获取到 HashEntry 对象后,其基于 next 属性构建的链表是不会发生变化的。
1 适配器模式:ListView或GridView的Adapter
简介;不同的数据提供者使用一个适配器来向一个相同的客户提供服务。
2 建造者模式:将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示。
简介;可以分步地构造每一部分。常见应用场景:Dialog的创建
3 观察者模式;‘EventBus’
简介;一个对象发生改变时,所有信赖于它的对象自动做相应改变。
常见应用场景:Rxjava的运用 ,ListView 刷新数据
4 策略模式;数据库
简介;定义了一系列封装了算法、行为的对象,他们可以相互替换。
5 单例模式;一般是指将消耗内存、属性和对象支持全局公用的对象,应该设置为单例模式,如持久化处理(网络、文件等)
常见应用场景:网络请求的工具类、sp存储的工具类、弹窗的工具类等
6 工厂模式:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
常见应用场景:activity的基类等
7 责任链模式: 将多个对象连成一条链,并沿着这条链传递该请求,只到有对象处理该请求为止。使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
常见应用场景:
1 Android 源码中对于事件分发是基于该模式,Android 会将事件包装成一个事件对象从ViewTree的顶部至上而下的分发传递,只到有View处理该事件为止。
2 OkHttp 的拦截器也是基于责任链模式,用户请求和服务器返回数据,会经过内置拦截器链逐级的处理。
把一些重要或重复的代码拿出来规范了一下而已
封装意义:
1.可拓展性强:若是你写的代码只适合某些特定、苛刻的条件下才能使用,那么这顶多算是一个工具类。反例:“封装”了一个BaseAdapter,但只能用在你的首页。ide
2.低耦合高内聚;在java里都必须遵循低耦合高内聚。反例:“封装”了一个WebActivity,主要是加载app的关于咱们、检查更新等h5的,但每次增长h5都要加上以下ifelse,以下。仔细想一想,只要上一个界面传入url和title就好了,根本不须要什么ifelse工具
3.简化了原有逻辑:代码可能会多(也不能太复杂吧),但逻辑思路要比原有清晰简单。反例:“封装”了一个BaseAdapter,但用户必需要额外调用不少set方法才能使用。动画
4.注释详细:若是没有注释只有你会玩那人生还有什么乐趣呢?反例:“封装”了一个超牛逼BaseAdapter,能够任意动画、任意填充数据,但没有任何注释,请问除了你谁敢用?url
5.源于需求:先有问题(可预知的问题也算)而后才能解决问题,封装也是这样。反例:“封装”了一个超牛逼BaseAdapter,能够上天,可是你的项目须要上天吗?code
6.可预见性:就是要有远见。好比公司的全部列表都展现10条,你不能在基类里面写死count=10,这应该由子类或动态生成的对象
7.不变性:虽然封装是源于需求,但封装必须尽可能不和业务需求打交道。不能因10条变成11条而大动干戈的改封装
字节流和字符流。
1 字节流继承于 InputStream 和 OutputStream,字符流继承于 InputStreamReader 和 OutputStreamWriter。
2 字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象
3 字节输出流转字符输出流通过 OutputStreamWriter 实现,该类的构造函数可以传入 OutputStream 对象
Handler 和 AsyncTask,还有 RunOnUiThread ,View.post(runnable) 注意子线程中能不能 new handler
如果在子线程中直接 new Handler()会抛出异常
其使用场景为进耗时操作后通知更新主线程,AsyncTask 内部也是 Handler 机制来完成的,只不过 Android 提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务,比如 HTTP 请求,大规模的下载和数据库的更改不适用于 AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生 AsyncTask 根本执行不了的问题 注意:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理。
1 Int是基本数据类型,默认值是0,直接存储数据
2 Integer是int类型的封装对象,声明需要实例化,是一个引用指向对象,默认null
1 "=="是判断两个变量或实例是不是指向同一个内存空间。
2 "equals"是判断两个变量或实例所指向的内存空间的值是不是相同。即: ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
3 “equals"在JAVA中是一个方法。”=="在JAVA中只是一个运算符合。
4 ==指引用是否相同, equals()指的是值是否相同
1 速度而言StringBuilder>StringBuffer>String,
2 String是常量,一旦创建便不可更改,所有的修改都是新建对象,而 StringBuilder和StringBuffer是变量,可以直接修改。
3 在线程安全上,StringBuffer是线程安全的,而StringBuilder是线程不安全的。
4 String 适用于少量字符串操作的情况,StringBuilder 适用于单线程下在字符缓冲 区进行大量操作的情况,StringBuffer 适用多线程下在字符缓冲区进行大量操作的
好记性不如烂笔头,这是近半年根据北京一些中小型企业安卓招聘需求整理的 Java 面试题,未完待续,如有不足欢迎留言指教