www.itlanbao.com 本网站里面分享很多Android特效,更多的面试资料在IT蓝豹资讯部分 ,希望能够给你帮助,欢迎到IT蓝豹上互相学习。
|
ArrayList |
Vector |
LinkedList |
实现原理 |
数组 |
数组 |
双向链表 |
线程安全 |
否 |
是 |
否 |
优点 |
1.数组实现优于遍历 |
1.数组实现优于遍历 |
1.节点的增删无需对象的重建 |
缺点 |
1.非线程安全 |
1.数组中未使用的元素造成空间的浪费 |
1.遍历效率较低 |
扩容 |
0.5倍增量 |
1倍增量 |
按需增删 |
使用场景 |
1.无线程的要求。 |
1.有线程安全的要求 |
增删场景较多的时候 |
11.int与Integer的区别
|
int |
Integer |
类型 |
基本类型 |
复合类型 |
默认值 |
0 |
null |
存储 |
栈(局部变量) |
堆上(只能通过new创建) |
方法 |
基本类型无方法 |
有 |
速度 |
快(栈上 的操作相对快) |
慢 |
泛型支持 |
否(java中的泛型不支持,C++中的模板支持) |
支持 |
容器类支持 |
否(直接使用通常会进行装箱操作) |
支持 |
存在意义 |
1.历史原因(顺延C/C++中存在) |
基本类型int的包装类 |
Checked Exception:在编译时就能够被Java编译器所检测到的。
UncheckedException:则是编译时,java编译器不能检查到。
|
RuntimeException |
普通Exception |
Error |
受控异常 |
否 |
是 |
否 |
产生原因 |
开发者的编程错误 |
由于外界环境所限, |
Java运行时的系统错误,资源耗尽,是一种严重的, |
例子 |
NullPointerException |
ClassNotFoundException |
VirtualMachineError |
final:关键字,表不变
修饰:
· 方法:方法不可Override
· 类:不可被继承
· 基本类型量:常量,值不可变
· 符合类型量:引用不可变,即引用的值不可变
[java] view plaincopy
1. final Object o1 = new Object();
2. o1 = new Object();
finally:关键字,Java异常处理机制的一部分,在异常发生时,用来提供一个必要的清理的机会。
finalize:Object类的方法(参考自百度百科)
意义:Java技术允许使用finalize()方法在垃圾回收器将对象回收之前,做一些必要的清理操作。
调用前提:这个对象确定没有被引用到。
工作原理:
· 垃圾收集器准备好释放对象占用的空间。
· 首先调用其finalize方法。
· 下一次垃圾收集过程中,真正回收内存。
不确定性:
· finalize的执行时间是不缺定的。
· 一个对象引用另一个对象,并不能保证finalize的方法按照特定的执行顺序。
|
Override |
Overload |
签名+返回值 |
相同 |
方法名相同,签名不同 |
关系 |
父子类继承关系 |
通常是同一类层次中 |
识别 |
运行时多态 |
编译时多态 |
修饰符限制 |
非private |
无特别 |
异常关系 |
子类方法不能抛出被父类方法更多的异常 |
无特别 |
可见性关系 |
子类不能比父类访问权限更窄 |
无特别 |
Collection:接口,集合类的接口,一个契约,提供了集合基本的大小,添加,清除,遍历方法等。
Collections:工具类,提供了很多静态方法,给集合提供一些查询,比较,排序,交换,线程安全化等方法。
package com.jue.test;
public class TestMain {
public static void main(String[] args) {
Integer i1 = 1;
Integer i11 = 1;
System.out.println(i1 == i11);
Integer i2 = 200;
Integer i22 = 200;
System.out.println(i2 == i22);
}
}
结果 :True,false
分析:反编译结果为 Integer i1 = Integer.valueOf(1);
可以看出,对于Integer i = 1;编译器做了额外的处理,即Integer.valueof();可以看出Integer对于一定 范围内的数字从Cache中取得,对于额外的,调用new创建。
故可以知道Integer的大小,默认是从-128到127,对于这个范围内的数组做了缓存的处理。 对于额外的,调用new创建
|
wait |
sleep |
所属类 |
Object |
Thread |
意义 |
让线程挂起 |
让线程休眠指定的时间 |
释放锁 |
是 |
否(这个跟锁本来就没有关系) |
恢复 |
1.有参:wait指定时间 |
1.根据参数长度自动恢复。 |
使用限制 |
wait,notify必须持有当前对象锁的情况下调用 |
无特别 |
抛出异常 |
否 |
是 |
静态方法 |
否 |
是 |
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
首先,创建二者的共同接口:
[java] view plaincopy
1. public interface Sender {
2. public void Send();
3. }
其次,创建实现类:
[java] view plaincopy
1. public class MailSender implements Sender {
2. @Override
3. public void Send() {
4. System.out.println("this is mailsender!");
5. }
6. }
[java] view plaincopy
1. public class SmsSender implements Sender {
2.
3. @Override
4. public void Send() {
5. System.out.println("this is sms sender!");
6. }
7. }
最后,建工厂类:
[java] view plaincopy
1. public class SendFactory {
2.
3. public Sender produce(String type) {
4. if ("mail".equals(type)) {
5. return new MailSender();
6. } else if ("sms".equals(type)) {
7. return new SmsSender();
8. } else {
9. System.out.println("请输入正确的类型!");
10. return null;
11. }
12. }
13. }
我们来测试下:
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. SendFactory factory = new SendFactory();
5. Sender sender = factory.produce("sms");
6. sender.Send();
7. }
8. }
输出:this is sms sender!
a、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
将上面的代码做下修改,改动下SendFactory类就行,如下:
[java] view plaincopypublic class SendFactory {
public Sender produceMail(){
1. return new MailSender();
2. }
3.
4. public Sender produceSms(){
5. return new SmsSender();
6. }
7. }
测试类如下:
[java] view plaincopy
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. SendFactory factory = new SendFactory();
5. Sender sender = factory.produceMail();
6. sender.Send();
7. }
8. }
输出:this is mailsender!
b、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
[java] view plaincopy
1. public class SendFactory {
2.
3. public static Sender produceMail(){
4. return new MailSender();
5. }
6.
7. public static Sender produceSms(){
8. return new SmsSender();
9. }
10. }
[java] view plaincopy
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. Sender sender = SendFactory.produceMail();
5. sender.Send();
6. }
7. }
输出:this is mailsender!
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传 入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实 现。
c、简单工厂和工厂方法模式的比较
工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。
d、抽象工厂模式
代码:
//抽象工厂类
public abstract class AbstractFactory {
public abstract Vehicle createVehicle();
public abstract Weapon createWeapon();
public abstract Food createFood();
}
//具体工厂类,其中Food,Vehicle,Weapon是抽象类,
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
//测试类
public class Test {
public static void main(String[] args) {
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
v.run();
Weapon w = f.createWeapon();
w.shoot();
Food a = f.createFood();
a.printName();
}
}
在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
六、总结。
(1)简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
(2)工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
(3)抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
首先我们写一个简单的单例类:
[java] view plaincopy
1. public class Singleton {
2.
3. /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
4. private static Singleton instance = null;
5.
6. /* 私有构造方法,防止被实例化 */
7. private Singleton() {
8. }
9.
10. /* 静态工程方法,创建实例 */
11. public static Singleton getInstance() {
12. if (instance == null) {
13. instance = new Singleton();
14. }
15. return instance;
16. }
17.
18. /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
19. public Object readResolve() {
20. return instance;
21. }
22. }
这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:
[java] view plaincopy
1. public static synchronized Singleton getInstance() {
2. if (instance == null) {
3. instance = new Singleton();
4. }
5. return instance;
6. }
但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:
[java] view plaincopy
1. public static Singleton getInstance() {
2. if (instance == null) {
3. synchronized (instance) {
4. if (instance == null) {
5. instance = new Singleton();
6. }
7. }
8. }
9. return instance;
10. }
1、最简单的实现
首先,能想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实例化此类。然后在类中返回一个静态示例并返回给调用者。这样,调用者就可以通过这个引用使用这个实例了。
public class Singleton{ private static final Singleton singleton = new Singleton(); public static Singleton getInstance(){ return singleton; } private Singleton(){ }}
如上例,外部使用者如果需要使用SingletonClass的实例,只能通过getInstance()方法,并且它的构造方法是private的,这样就保证了只能有一个对象存在。
2、性能优化--lazy loaded
上面的代码虽然简单,但是有一个问题----无论这个类是否被使用,都会创建一个instance对象。如果这个创建很耗时,比如说链接10000次数据库(夸张一点啦....),并且这个类还不一定会被使用,那么这个创建过程就是无用的,怎么办呢?
为了解决这个问题,我们想到的新的解决方案:
public class SingletonClass { private static SingletonClass instance = null; public static SingletonClass getInstance() { if(instance == null) { instance = new SingletonClass(); } return instance; } private SingletonClass() { } }
代码的变化有俩处----首先,把 instance 设置为 null ,知道第一次使用的时候判是否为 null 来创建对象。因为创建对象不在声明处,所以那个 final 的修饰必须去掉。
我们来想象一下这个过程。要使用 SingletonClass ,调用 getInstance()方法,第一次的时候发现instance时null,然后就创建一个对象,返回出去;第二次再使用的时候,因为这个 instance事static的,共享一个对象变量的,所以instance的值已经不是null了,因此不会再创建对象,直接将其返回。
这个过程就称为lazy loaded ,也就是迟加载-----直到使用的时候才经行加载。
这样写法也比较完美:但是还可以优化
public class SingletonClass{ private static SingletonClass instance = null; public static SingletonClass getInstance(){ if(instance == null){ synchronized(SingletonClass.class){ if(instance == null){ instance = new SingletonClass(); } } } return instance; } private SingletonClass(){ } }
通过单例模式的学习告诉我们:
1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。
2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。
到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?
首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)
其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。
再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。
最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类 不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很 大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事 情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码:
还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:
[java] view plaincopy
1. public class Builder {
2.
3. private List<Sender> list = new ArrayList<Sender>();
4.
5. public void produceMailSender(int count){
6. for(int i=0; i<count; i++){
7. list.add(new MailSender());
8. }
9. }
10.
11. public void produceSmsSender(int count){
12. for(int i=0; i<count; i++){
13. list.add(new SmsSender());
14. }
15. }
16. }
测试类:
[java] view plaincopy
1. public class Test {
2.
3. public static void main(String[] args) {
4. Builder builder = new Builder();
5. builder.produceMailSender(10);
6. }
7. }
从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。在Java中,复制对象是通过clone()实现的,先创建一个原型类:
[java] view plaincopy
1. public class Prototype implements Cloneable {
2.
3. public Object clone() throws CloneNotSupportedException {
4. Prototype proto = (Prototype) super.clone();
5. return proto;
6. }
7. }
很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口 是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句 话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,我会 在另一篇文章中,关于解读Java中本地方法的调用,此处不再深究。在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
此处,写一个深浅复制的例子:
[java] view plaincopy
1. public class Prototype implements Cloneable, Serializable {
2.
3. private static final long serialVersionUID = 1L;
4. private String string;
5.
6. private SerializableObject obj;
7.
8. /* 浅复制 */
9. public Object clone() throws CloneNotSupportedException {
10. Prototype proto = (Prototype) super.clone();
11. return proto;
12. }
13.
14. /* 深复制 */
15. public Object deepClone() throws IOException, ClassNotFoundException {
16.
17. /* 写入当前对象的二进制流 */
18. ByteArrayOutputStream bos = new ByteArrayOutputStream();
19. ObjectOutputStream oos = new ObjectOutputStream(bos);
20. oos.writeObject(this);
21.
22. /* 读出二进制流产生的新对象 */
23. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
24. ObjectInputStream ois = new ObjectInputStream(bis);
25. return ois.readObject();
26. }
27.
28. public String getString() {
29. return string;
30. }
31.
32. public void setString(String string) {
33. this.string = string;
34. }
35.
36. public SerializableObject getObj() {
37. return obj;
38. }
39.
40. public void setObj(SerializableObject obj) {
41. this.obj = obj;
42. }
43.
44. }
45.
46. class SerializableObject implements Serializable {
47. private static final long serialVersionUID = 1L;
48. }
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,我们看下面的图:
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。首先,我们来看看类的适配器模式,先看类图:
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:
[java] view plaincopy
1. public class Source {
2.
3. public void method1() {
4. System.out.println("this is original method!");
5. }
6. }
[java] view plaincopy
1. public interface Targetable {
2.
3. /* 与原类中的方法相同 */
4. public void method1();
5.
6. /* 新类的方法 */
7. public void method2();
8. }
[java] view plaincopy
1. public class Adapter extends Source implements Targetable {
2.
3. @Override
4. public void method2() {
5. System.out.println("this is the targetable method!");
6. }
7. }
Adapter类继承Source类,实现Targetable接口,下面是测试类:
[java] view plaincopy
1. public class AdapterTest {
2.
3. public static void main(String[] args) {
4. Targetable target = new Adapter();
5. target.method1();
6. target.method2();
7. }
8. }
输出:
this is original method!
this is the targetable method!
这样Targetable接口的实现类就具有了Source类的功能
将之前介绍的所有排序算法整理成NumberSort类,代码
NumberSort
Java语言引入了Java虚拟机,具有跨平台运行的功能,能够很好地适应各种Web应用。同时,为了提高Java语言的性能和健壮性,还引入了如垃圾回收机制等新功能,通过这些改进让Java具有其独特的工作原理。
1.Java虚拟机
Java源程序通过编译器编译成.Class文件,然后java虚拟机中的java 解释器负责将字节码文件解释成为特定的机器码进行运行。
java是一种半编译半解释型语言。半编译是指:java源代码,会经过javac命令变成 .class文件。半解释是指: .class文件被jvm解释的过程。也就是因为jvm的半解释才有了java的动态语言特性:反射和annotation。
和android区别
alvik有自己的libdex库负责对.class进行处理。libdex主要对.class进行处理生成自己的dex文件。主要做的工作是,对虚拟机指令进行转换(dalvik是基于寄存器的,sun虚拟机是基于栈的),对类的静态数据进行归类、压缩。
dalvik基于寄存器,而JVM基于stack ,Dalvik执行的是特有的DEX文件格式,而JVM运行的是*.class文件格式。
优势:1、在编译时提前优化代码而不是等到运行时
2、 虚拟机很小,使用的空间也小;被设计来满足可高效运行多种虚拟机实例。
Java虚拟机的建立需要针对不同的软硬件平台来实现,既要考虑处理器的型号,也要考虑操作系统的种类。由此在SPARC结构、X86结构、MIPS和PPC等嵌入式处理芯片上,在UNIX、Linux、Windows和部分实时操作系统上都可实现Java虚拟机。
2.无用内存自动回收机制
而在Java运行环境中,始终存在着一个系统级的线程,专门跟踪内存的使用情况, 定期检测出不再使用的内存,并自动进行回收,避免了内存的泄露,也减轻了程序员的工作量。
1.JVM
JVM是Java平台的核心,为了让编译产生的字节码能更好地解释与执行,因此把JVM分成了6个部分:JVM解释器、指令系统、寄存器、栈、存储区和碎片回收区。
可以很明显看出,Android系统架构由5部分组成,分别是:Linux Kernel、Android Runtime、Libraries、Application Framework、Applications。第二部分将详细介绍这5个部分。
现在我们拿起手术刀来剖析各个部分。其实这部分SDK文档已经帮我们做得很好了,我们要做的就是拿来主义,然后再加上自己理解。下面自底向上分析各层。
Android基于Linux 2.6提供核心系统服务,例如:安全、内存管理、进程管理、网络堆栈、驱动模型。Linux Kernel也作为硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务。
Android 包含一个核心库的集合,提供大部分在Java编程语言核心类库中可用的功能。每一个Android应用程序是Dalvik虚拟机中的实例,运行在他们自己 的进程中。Dalvik虚拟机设计成,在一个设备可以高效地运行多个虚拟机。Dalvik虚拟机可执行文件格式是.dex,dex格式是专为Dalvik 设计的一种压缩格式,适合内存和处理器速度有限的系统。
大多数虚拟机包括JVM都是基于栈的,而Dalvik虚拟机则是基于寄存器的。 两种架构各有优劣,一般而言,基于栈的机器需要更多指令,而基于寄存器的机器指令更大。dx 是一套工具,可以將 Java .class 转换成 .dex 格式。一个dex文件通常会有多个.class。由于dex有時必须进行最佳化,会使文件大小增加1-4倍,以ODEX结尾。
Dalvik虚拟机依赖于Linux 内核提供基本功能,如线程和底层内存管理。
Android包含一个C/C++库的集合,供Android系统的各个组件使用。这些功能通过Android的应用程序框架(application framework)暴露给开发者。下面列出一些核心库:
· 系统C库——标准C系统库(libc)的BSD衍生,调整为基于嵌入式Linux设备
· 媒体库——基于PacketVideo的OpenCORE。这些库支持播放和录制许多流行的音频和视频格式,以及静态图像文件,包括MPEG4、 H.264、 MP3、 AAC、 AMR、JPG、 PNG
· 界面管理——管理访问显示子系统和无缝组合多个应用程序的二维和三维图形层
· LibWebCore——新式的Web浏览器引擎,驱动Android 浏览器和内嵌的web视图
· SGL——基本的2D图形引擎
· 3D库——基于OpenGL ES 1.0 APIs的实现。库使用硬件3D加速或包含高度优化的3D软件光栅
· FreeType ——位图和矢量字体渲染
· SQLite ——所有应用程序都可以使用的强大而轻量级的关系数据库引擎
通过提供开放的开发平台,Android使开发者能够编制极其丰富和新颖的应用程序。开发者可以自由地利用设备硬件优势、访问位置信息、运行后台服务、设置闹钟、向状态栏添加通知等等,很多很多。
开发者可以完全使用核心应用程序所使用的框架APIs。应用程序的体系结构旨在简化组件的重用,任何应用程序都能发布他的功能且任何其他应用程序可以使用这些功能(需要服从框架执行的安全限制)。这一机制允许用户替换组件。
所有的应用程序其实是一组服务和系统,包括:
· 视图(View)——丰富的、可扩展的视图集合,可用于构建一个应用程序。包括包括列表、网格、文本框、按钮,甚至是内嵌的网页浏览器
· 内容提供者(Content Providers)——使应用程序能访问其他应用程序(如通讯录)的数据,或共享自己的数据
· 资源管理器(Resource Manager)——提供访问非代码资源,如本地化字符串、图形和布局文件
· 通知管理器(Notification Manager)——使所有的应用程序能够在状态栏显示自定义警告
· 活动管理器(Activity Manager)——管理应用程序生命周期,提供通用的导航回退功能
Android装配一个核心应用程序集合,包括电子邮件客户端、SMS程序、日历、地图、浏览器、联系人和其他设置。所有应用程序都是用Java编程语言写的。更加丰富的应用程序有待我们去开发!
一、Socket通信简介
Android 与服务器的通信方式主要有两种,一是Http通信,一是Socket通信。两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立 连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据。而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可 实现信息的主动推送,而不需要每次由客户端想服务器发送请求。 那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信。通过建立socket连接,可为通信双方的数据传输 传提供通道。socket的主要特点有数据丢失率低,使用简单且易于移植。
1.2Socket的分类
根据不同的的底层协议,Socket的实现是多样化的。本指南中只介绍TCP/IP协议族的内容,在这个协议族当中主要的Socket类型为流套接字 (streamsocket)和数据报套接字(datagramsocket)。流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。数据 报套接字使用UDP协议,提供数据打包发送服务。
二、Socket 基本通信模型
三、Socket基本实现原理
3.1基于TCP协议的Socket
服务器端首先声明一个ServerSocket对象并且指定端口号,然后调 用Serversocket的accept()方法接收客户端的数据。accept()方法在没有数据进行接收的处于堵塞状态。 (Socketsocket=serversocket.accept()),一旦接收到数据,通过inputstream读取接收的数据。
客户端创建一个Socket对象,指定服务器端的ip地址和端口号 (Socketsocket=newSocket("172.168.10.108",8080);),通过inputstream读取数据,获取服务器 发出的数据(OutputStreamoutputstream=socket.getOutputStream()),最后将要发送的数据写入到 outputstream即可进行TCP协议的socket数据传输。
3.2基于UDP协议的数据传输
服务器端首先创建一个DatagramSocket对象,并且指点监听的端 口。接下来创建一个空的DatagramSocket对象用于接收数据 (bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)), 使用DatagramSocket的receive方法接收客户端发送的数据,receive()与serversocket的accepet()类似, 在没有数据进行接收的处于堵塞状态。
客户端也创建个DatagramSocket对象,并且指点监听的端口。接 下来创建一个InetAddress对象,这个对象类似与一个网络的发送地址 (InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定义要发送的 一个字符串,创建一个DatagramPacket对象,并制定要讲这个数据报包发送到网络的那个地址以及端口号,最后使用DatagramSocket 的对象的send()发送数据。*(Stringstr="hello";bytedata[]=str.getByte(); DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)
四、android 实现socket简单通信
4.1使用TCP协议通信
android端实现:
[java] view plaincopy
1. protected void connectServerWithTCPSocket() {
2.
3. Socket socket;
4. try {// 创建一个Socket对象,并指定服务端的IP及端口号
5. socket = new Socket("192.168.1.32", 1989);
6. // 创建一个InputStream用户读取要发送的文件。
7. InputStream inputStream = new FileInputStream("e://a.txt");
8. // 获取Socket的OutputStream对象用于发送数据。
9. OutputStream outputStream = socket.getOutputStream();
10. // 创建一个byte类型的buffer字节数组,用于存放读取的本地文件
11. byte buffer[] = new byte[4 * 1024];
12. int temp = 0;
13. // 循环读取文件
14. while ((temp = inputStream.read(buffer)) != -1) {
15. // 把数据写入到OuputStream对象中
16. outputStream.write(buffer, 0, temp);
17. }
18. // 发送读取的数据到服务端
19. outputStream.flush();
20.
21. /** 或创建一个报文,使用BufferedWriter写入,看你的需求 **/
22. // String socketData = "[2143213;21343fjks;213]";
23. // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
24. // socket.getOutputStream()));
25. // writer.write(socketData.replace("\n", " ") + "\n");
26. // writer.flush();
27. /************************************************/
28. } catch (UnknownHostException e) {
29. e.printStackTrace();
30. } catch (IOException e) {
31. e.printStackTrace();
32. }
33.
34. }
服务器端简单实现:
[java] view plaincopy
1. public void ServerReceviedByTcp() {
2. // 声明一个ServerSocket对象
3. ServerSocket serverSocket = null;
4. try {
5. // 创建一个ServerSocket对象,并让这个Socket在1989端口监听
6. serverSocket = new ServerSocket(1989);
7. // 调用ServerSocket的accept()方法,接受客户端所发送的请求,
8. // 如果客户端没有发送数据,那么该线程就停滞不继续
9. Socket socket = serverSocket.accept();
10. // 从Socket当中得到InputStream对象
11. InputStream inputStream = socket.getInputStream();
12. byte buffer[] = new byte[1024 * 4];
13. int temp = 0;
14. // 从InputStream当中读取客户端所发送的数据
15. while ((temp = inputStream.read(buffer)) != -1) {
16. System.out.println(new String(buffer, 0, temp));
17. }
18. serverSocket.close();
19. } catch (IOException e) {
20. e.printStackTrace();
21. }
22. }
4.2使用UDP协议通信
客户端发送数据实现:
[java] view plaincopy
1. protected void connectServerWithUDPSocket() {
2.
3. DatagramSocket socket;
4. try {
5. //创建DatagramSocket对象并指定一个端口号,注意,如果客户端需要接收服务器的返回数据,
6. //还需要使用这个端口号来receive,所以一定要记住
7. socket = new DatagramSocket(1985);
8. //使用InetAddress(Inet4Address).getByName把IP地址转换为网络地址
9. InetAddress serverAddress = InetAddress.getByName("192.168.1.32");
10. //Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.1.32");
11. String str = "[2143213;21343fjks;213]";//设置要发送的报文
12. byte data[] = str.getBytes();//把字符串str字符串转换为字节数组
13. //创建一个DatagramPacket对象,用于发送数据。
14. //参数一:要发送的数据 参数二:数据的长度 参数三:服务端的网络地址 参数四:服务器端端口号
15. DatagramPacket packet = new DatagramPacket(data, data.length ,serverAddress ,10025);
16. socket.send(packet);//把数据发送到服务端。
17. } catch (SocketException e) {
18. e.printStackTrace();
19. } catch (UnknownHostException e) {
20. e.printStackTrace();
21. } catch (IOException e) {
22. e.printStackTrace();
23. }
24. }
客户端接收服务器返回的数据:
[java] view plaincopy
1. public void ReceiveServerSocketData() {
2. DatagramSocket socket;
3. try {
4. //实例化的端口号要和发送时的socket一致,否则收不到data
5. socket = new DatagramSocket(1985);
6. byte data[] = new byte[4 * 1024];
7. //参数一:要接受的data 参数二:data的长度
8. DatagramPacket packet = new DatagramPacket(data, data.length);
9. socket.receive(packet);
10. //把接收到的data转换为String字符串
11. String result = new String(packet.getData(), packet.getOffset(),
12. packet.getLength());
13. socket.close();//不使用了记得要关闭
14. System.out.println("the number of reveived Socket is :" + flag
15. + "udpData:" + result);
16. } catch (SocketException e) {
17. e.printStackTrace();
18. } catch (IOException e) {
19. e.printStackTrace();
20. }
21. }
服务器接收客户端实现:
[java] view plaincopy
1. public void ServerReceviedByUdp(){
2. //创建一个DatagramSocket对象,并指定监听端口。(UDP使用DatagramSocket)
3. DatagramSocket socket;
4. try {
5. socket = new DatagramSocket(10025);
6. //创建一个byte类型的数组,用于存放接收到得数据
7. byte data[] = new byte[4*1024];
8. //创建一个DatagramPacket对象,并指定DatagramPacket对象的大小
9. DatagramPacket packet = new DatagramPacket(data,data.length);
10. //读取接收到得数据
11. socket.receive(packet);
12. //把客户端发送的数据转换为字符串。
13. //使用三个参数的String方法。参数一:数据包 参数二:起始位置 参数三:数据包长
14. String result = new String(packet.getData(),packet.getOffset() ,packet.getLength());
15. } catch (SocketException e) {
16. e.printStackTrace();
17. } catch (IOException e) {
18. e.printStackTrace();
19. }
20. }
五、总结:
使用UDP方式android端和服务器端接收可以看出,其实android端和服务器端的发送和接收大庭相径,只要端口号正确了,相互通信就没有问题,TCP使用的是流的方式发送,UDP是以包的形式发送。
本示例以Servlet为例,演示Android与Servlet的通信。
众所周知,Android与服务器通信通常采用HTTP通信方式和Socket通信方式,而HTTP通信方式又分get和post两种方式。至于Socket通信会在以后的博文中介绍。
HTTP协议简介:
HTTP (Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种协议。
HTTP连接最显 著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。 在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
由 于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”、“无状态”,要保持客户端程序的在线状态,需要不断地向服务器发起连接 请求。通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回 复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
基于HTTP1.0协议的客户端在每次向服务器发出请求后,服务器就会向客户端返回响应消息,在确认客户端已经收到响应消息后,服务端就会关闭网络连接。在这个数据传输过程中,并不保存任何历史信息和状态信息,因此,HTTP协议也被认为是无状态的协议。
HTTP1.1 和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持。当客户端使用HTTP1.1协议连接到服务器后,服务器就将关闭客户端连接的主动权交还 给客户端;也就是说,只要不调用Socket类的close方法关闭网络连接,就可以继续向服务器发送HTTP请求。
HTTP连接使用的是“请求—响应”的方式(2次握手),不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。而Socket连接在双方建立起连接后就可以直接进行数据的传输
HTTP协议的特点:
支持B/S及C/S模式;
简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由Content-Type 加以标记;
无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
HTTP协议请求方法:
请求行中包括了请求方法,解释如下:
GET 请求获取Request-URI 所标识的资源;
POST 在Request-URI 所标识的资源后附加新的数据;
HEAD 请求获取由Request-URI 所标识的资源的响应消息报头
PUT 请求服务器存储一个资源,并用Request-URI 作为其标识
DELETE 请求服务器删除Request-URI 所标识的资源;
TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT 保留将来使用
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求
Get与Post请求区别:
Post 请求可以向服务器传送数据,而且数据放在HTML HEADER内一起传送到服务端URL地址,数据对用户不可见。而get是把参数数据队列加到提交的URL中,值和表单内各个字段一一对应, 例如(http://www.baidu.com/s?w=%C4&inputT=2710)
get 传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
get安全性非常低,post安全性较高。
在Android开发中我们经常会用到网络连接功能与服务器进行数据的交互,为此Android的SDK提供了Apache的HttpClient来方便我们使用各种Http服务
///
Get() 先创建一个HttpClient 然后再创建一个HttpGet,通过HttpClient的execute方法来发送一个HttpGet并且返回String内容。
try {
// 创建一个默认的HttpClient
HttpClient httpclient =new DefaultHttpClient();
// 创建一个GET请求
HttpGet request =new HttpGet("www.google.com");
// 发送GET请求,并将响应内容转换成字符串
String response = httpclient.execute(request, new BasicResponseHandler());
Log.v("response text", response);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
///////////////////////////////
Post()
publicstatic String post(String url, NameValuePair... params) {
try {
// 编码参数
List<NameValuePair> formparams =new ArrayList<NameValuePair>(); // 请求参数
for (NameValuePair p : params) {
formparams.add(p);
}
UrlEncodedFormEntity entity =new UrlEncodedFormEntity(formparams,
CHARSET);
// 创建POST请求
HttpPost request =new HttpPost(url);
request.setEntity(entity);
// 发送请求
HttpClient client = getHttpClient();
HttpResponse response = client.execute(request);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
thrownew RuntimeException("请求失败");
}
HttpEntity resEntity = response.getEntity();
return (resEntity ==null) ?null : EntityUtils.toString(resEntity, CHARSET);
} catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage());
returnnull;
} catch (ClientProtocolException e) {
Log.w(TAG, e.getMessage());
returnnull;
} catch (IOException e) {
thrownew RuntimeException("连接失败", e);
}
}
整理:杨光福&王飞龙
时间仓促,难免有不周之处,请见谅。
http://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html
可以用我们上课讲的说
也可以参照
http://blog.saymagic.cn/2015/01/30/android-pic-three-cache.html
http://www.cnblogs.com/elliotta/p/3633752.html
http://blog.csdn.net/yudajun/article/details/9323941
技术方面说明
http://blog.csdn.net/lwyygydx/article/details/41870377
功能改进方面说明
http://digi.tech.qq.com/a/20150121/012030.htm
可以参照李延磊老师的
也可以参照链接
http://blog.csdn.net/fancylovejava/article/details/39927539
xUtils,Gson 极光推送 都讲过,忽略
友盟第三方登录
http://blog.umeng.com/uncategorized/4160.html
第三方登录案例
http://blog.csdn.net/yueqinglkong/article/details/15028041
http://blog.csdn.net/lyf_007217/article/details/8542238
http://www.cnblogs.com/devinzhang/p/3856200.html
http://www.360doc.com/content/14/0402/09/10504424_365635496.shtml
http://blog.csdn.net/androidzhaoxiaogang/article/details/7910364
http://www.2cto.com/kf/201409/335964.html
http://blog.csdn.net/u200814499/article/details/40391443
http://blog.csdn.net/jiangliloveyou/article/details/9849775
http://blog.csdn.net/lnb333666/article/details/7471292
http://skywen.iteye.com/blog/1811310
已经讲了,请看视频
已经讲了,请看视频
已经讲了,请看视频
http://blog.csdn.net/wuqiong_524itcast/article/details/25378685
http://blog.csdn.net/wangshione/article/details/8490245
http://blog.csdn.net/lnb333666/article/details/8031770
1.垃圾收集算法的核心思想
Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则 为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。
2.触发主GC(Garbage Collector)的条件
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以 便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
3.减少GC开销的措施
根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:
(1)不要显式调用System.gc()
此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。
(2)尽量减少临时对象的使用
临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
(3)对象不用时最好显式置为Null
一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。
(4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)
由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如 Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但 这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。
(5)能用基本类型如Int,Long,就不用Integer,Long对象
基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。
(6)尽量少用静态对象变量
静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
(7)分散对象创建或删除的时间
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片, 从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC 的机会。
gc()函数的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。在Davilk中,给程序分配的内存是根据机型厂商的不同而不同(现在大部分为32MB),在VM内部会将内存分为:java使用的内存,Native使用的内存,他们之间不能共享,当某一方面不足
的时候必须向VM申请,而不能直接使用另外一个的内存。
出现内存泄漏的可能性:
出现情况:
1. 数据库的cursor没有关闭
2.构造adapter时,没有使用缓存contentview
衍生listview的优化问题-----减少创建view的对象,充分使用contentview,可以使用一静态类来优化处理getview的过程
3.Bitmap对象不使用时采用recycle()释放内存
4.activity中的对象的生命周期大于activity
调试方法: DDMS==> HEAPSZIE==>dataobject==>[Total Size]
一、 Android的内存机制
Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java 堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清 洁工(GC)
二、GC是什么? 为什么要有GC?
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以 自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
四、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对 象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。可以。程序员可 以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对 象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象 数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。
Java带垃圾回收的机制,为什么还会内存泄露呢?举例:
[java] view plaincopyprint?
1. Vector v = new Vector(10);
2. for (int i = 1; i < 100; i++) {
3. Object o = new Object();
4. v.add(o);
5. o = null;
6. }// 此时,所有的Object对象都没有被释放,因为变量v引用这些对象。
Java 内存泄露的根本原因就是 保存了不可能再被访问的变量类型的引用
六、Android的内存溢出
Android的内存溢出是如何发生的?
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。也就是说我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。
为什么会出现内存不够用的情况呢?我想原因主要有两个:
由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。
(一)、(二)中,我们了解了一些基本概念。
600dp的含义是:代表这个设备的最短的那一边。
获取设备的最短边的代码是:Configuration config = getResources().getConfiguration();
int smallestScreenWidth = config.smallestScreenWidthDp;
这个时候拿smallestScreenWidth 与600想比较就可以知道该设备能否读取里面的资源了。
)
除此之外,为了方便适配,在编码时我们还应该注意什么呢,主要有以下几点:
(1)多使用权重(android:layout_weight)
尤其是在tab切换布局,listview title及Item布局等情况下;
(2)设置宽度和高度时,尽量使用match_parent和wrap_content,避免把控件宽高设死;
(3)父容器布局选用
多使用RelativeLayout,FrameLayout,GridLayout等,减少布局层次。当然,在使用
权重时,得采用LinearLayout;
(4) 在xml里,设置高度、宽度采用dp(dip),设置字体采用sp。
(应该注意,在代码里面,我们写的setHeight(...)单位是px)
(二)
那么在具体开发中,我们应该注意什么呢。
首先,我们必须要知道,其实适配的关键在于两点:
(1)不同分辨率设备的适配,这点在单位的使用上用dp、sp以及图片资源存放于不同的drawable文件夹就可以解决问题;
(2)不同尺寸的适配,这点主要靠将相关值以及布局文件放置于不同的文件夹中来解决。
2.1 values文件夹
可以在工程下创建不同的values文件夹:values-sw480dp, values-sw600dp,
values-sw720dp-land等。比如一个控件的宽度,在10寸pad上是10dp,在8寸pad
上是5dp。这时,你可以定义一个变量,button_width,然后在values-sw600dp
下写5dp,在values-sw720-land下写
10dp。这样就达到了在不同尺寸pad上,
相应控件大小不一样的效果。
2.1 layout文件夹
如果在不同尺寸设备上展示的布局有明显差别,仅仅用values不同已经难以控制,
那么就可以考虑写不同的布局文件置于不同的layout文件夹下,android会根据设备
尺寸去加载相应文件夹下的布局文件。如:layout-sw480dp,layout-sw600dp,
layout-sw700dp等。
值得注意的是,如果不是很有必要,尽量采用2.1方案,方便维护。如果尺寸和分辨率都不同,
那么就要结合(1)、(2)考虑了。
(补充:其实values文件夹和layout文件夹不仅仅是根据尺寸判断,也和分辨率有关,不过在通常情况下,
综合计算考虑,仅根据尺寸判断就可以了:
www.itlanbao.com 本网站里面分享很多android特效,更多的面试资料在IT蓝豹资讯部分 ,希望能够给你帮助,欢迎到IT蓝豹上互相学习。
|
ArrayList |
Vector |
LinkedList |
实现原理 |
数组 |
数组 |
双向链表 |
线程安全 |
否 |
是 |
否 |
优点 |
1.数组实现优于遍历 |
1.数组实现优于遍历 |
1.节点的增删无需对象的重建 |
缺点 |
1.非线程安全 |
1.数组中未使用的元素造成空间的浪费 |
1.遍历效率较低 |
扩容 |
0.5倍增量 |
1倍增量 |
按需增删 |
使用场景 |
1.无线程的要求。 |
1.有线程安全的要求 |
增删场景较多的时候 |
11.int与Integer的区别
|
int |
Integer |
类型 |
基本类型 |
复合类型 |
默认值 |
0 |
null |
存储 |
栈(局部变量) |
堆上(只能通过new创建) |
方法 |
基本类型无方法 |
有 |
速度 |
快(栈上 的操作相对快) |
慢 |
泛型支持 |
否(java中的泛型不支持,C++中的模板支持) |
支持 |
容器类支持 |
否(直接使用通常会进行装箱操作) |
支持 |
存在意义 |
1.历史原因(顺延C/C++中存在) |
基本类型int的包装类 |
Checked Exception:在编译时就能够被Java编译器所检测到的。
UncheckedException:则是编译时,java编译器不能检查到。
|
RuntimeException |
普通Exception |
Error |
受控异常 |
否 |
是 |
否 |
产生原因 |
开发者的编程错误 |
由于外界环境所限, |
Java运行时的系统错误,资源耗尽,是一种严重的, |
例子 |
NullPointerException |
ClassNotFoundException |
VirtualMachineError |
final:关键字,表不变
修饰:
· 方法:方法不可Override
· 类:不可被继承
· 基本类型量:常量,值不可变
· 符合类型量:引用不可变,即引用的值不可变
[java] view plaincopy
1. final Object o1 = new Object();
2. o1 = new Object();
finally:关键字,Java异常处理机制的一部分,在异常发生时,用来提供一个必要的清理的机会。
finalize:Object类的方法(参考自百度百科)
意义:Java技术允许使用finalize()方法在垃圾回收器将对象回收之前,做一些必要的清理操作。
调用前提:这个对象确定没有被引用到。
工作原理:
· 垃圾收集器准备好释放对象占用的空间。
· 首先调用其finalize方法。
· 下一次垃圾收集过程中,真正回收内存。
不确定性:
· finalize的执行时间是不缺定的。
· 一个对象引用另一个对象,并不能保证finalize的方法按照特定的执行顺序。
|
Override |
Overload |
签名+返回值 |
相同 |
方法名相同,签名不同 |
关系 |
父子类继承关系 |
通常是同一类层次中 |
识别 |
运行时多态 |
编译时多态 |
修饰符限制 |
非private |
无特别 |
异常关系 |
子类方法不能抛出被父类方法更多的异常 |
无特别 |
可见性关系 |
子类不能比父类访问权限更窄 |
无特别 |
Collection:接口,集合类的接口,一个契约,提供了集合基本的大小,添加,清除,遍历方法等。
Collections:工具类,提供了很多静态方法,给集合提供一些查询,比较,排序,交换,线程安全化等方法。
package com.jue.test;
public class TestMain {
public static void main(String[] args) {
Integer i1 = 1;
Integer i11 = 1;
System.out.println(i1 == i11);
Integer i2 = 200;
Integer i22 = 200;
System.out.println(i2 == i22);
}
}
结果 :True,false
分析:反编译结果为 Integer i1 = Integer.valueOf(1);
可以看出,对于Integer i = 1;编译器做了额外的处理,即Integer.valueof();可以看出Integer对于一定 范围内的数字从Cache中取得,对于额外的,调用new创建。
故可以知道Integer的大小,默认是从-128到127,对于这个范围内的数组做了缓存的处理。 对于额外的,调用new创建
|
wait |
sleep |
所属类 |
Object |
Thread |
意义 |
让线程挂起 |
让线程休眠指定的时间 |
释放锁 |
是 |
否(这个跟锁本来就没有关系) |
恢复 |
1.有参:wait指定时间 |
1.根据参数长度自动恢复。 |
使用限制 |
wait,notify必须持有当前对象锁的情况下调用 |
无特别 |
抛出异常 |
否 |
是 |
静态方法 |
否 |
是 |
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
首先,创建二者的共同接口:
[java] view plaincopy
1. public interface Sender {
2. public void Send();
3. }
其次,创建实现类:
[java] view plaincopy
1. public class MailSender implements Sender {
2. @Override
3. public void Send() {
4. System.out.println("this is mailsender!");
5. }
6. }
[java] view plaincopy
1. public class SmsSender implements Sender {
2.
3. @Override
4. public void Send() {
5. System.out.println("this is sms sender!");
6. }
7. }
最后,建工厂类:
[java] view plaincopy
1. public class SendFactory {
2.
3. public Sender produce(String type) {
4. if ("mail".equals(type)) {
5. return new MailSender();
6. } else if ("sms".equals(type)) {
7. return new SmsSender();
8. } else {
9. System.out.println("请输入正确的类型!");
10. return null;
11. }
12. }
13. }
我们来测试下:
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. SendFactory factory = new SendFactory();
5. Sender sender = factory.produce("sms");
6. sender.Send();
7. }
8. }
输出:this is sms sender!
a、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
将上面的代码做下修改,改动下SendFactory类就行,如下:
[java] view plaincopypublic class SendFactory {
public Sender produceMail(){
1. return new MailSender();
2. }
3.
4. public Sender produceSms(){
5. return new SmsSender();
6. }
7. }
测试类如下:
[java] view plaincopy
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. SendFactory factory = new SendFactory();
5. Sender sender = factory.produceMail();
6. sender.Send();
7. }
8. }
输出:this is mailsender!
b、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
[java] view plaincopy
1. public class SendFactory {
2.
3. public static Sender produceMail(){
4. return new MailSender();
5. }
6.
7. public static Sender produceSms(){
8. return new SmsSender();
9. }
10. }
[java] view plaincopy
1. public class FactoryTest {
2.
3. public static void main(String[] args) {
4. Sender sender = SendFactory.produceMail();
5. sender.Send();
6. }
7. }
输出:this is mailsender!
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传 入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实 现。
c、简单工厂和工厂方法模式的比较
工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。
d、抽象工厂模式
代码:
//抽象工厂类
public abstract class AbstractFactory {
public abstract Vehicle createVehicle();
public abstract Weapon createWeapon();
public abstract Food createFood();
}
//具体工厂类,其中Food,Vehicle,Weapon是抽象类,
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
//测试类
public class Test {
public static void main(String[] args) {
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
v.run();
Weapon w = f.createWeapon();
w.shoot();
Food a = f.createFood();
a.printName();
}
}
在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
六、总结。
(1)简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
(2)工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
(3)抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
首先我们写一个简单的单例类:
[java] view plaincopy
1. public class Singleton {
2.
3. /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
4. private static Singleton instance = null;
5.
6. /* 私有构造方法,防止被实例化 */
7. private Singleton() {
8. }
9.
10. /* 静态工程方法,创建实例 */
11. public static Singleton getInstance() {
12. if (instance == null) {
13. instance = new Singleton();
14. }
15. return instance;
16. }
17.
18. /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
19. public Object readResolve() {
20. return instance;
21. }
22. }
这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:
[java] view plaincopy
1. public static synchronized Singleton getInstance() {
2. if (instance == null) {
3. instance = new Singleton();
4. }
5. return instance;
6. }
但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:
[java] view plaincopy
1. public static Singleton getInstance() {
2. if (instance == null) {
3. synchronized (instance) {
4. if (instance == null) {
5. instance = new Singleton();
6. }
7. }
8. }
9. return instance;
10. }
1、最简单的实现
首先,能想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实例化此类。然后在类中返回一个静态示例并返回给调用者。这样,调用者就可以通过这个引用使用这个实例了。
public class Singleton{ private static final Singleton singleton = new Singleton(); public static Singleton getInstance(){ return singleton; } private Singleton(){ }}
如上例,外部使用者如果需要使用SingletonClass的实例,只能通过getInstance()方法,并且它的构造方法是private的,这样就保证了只能有一个对象存在。
2、性能优化--lazy loaded
上面的代码虽然简单,但是有一个问题----无论这个类是否被使用,都会创建一个instance对象。如果这个创建很耗时,比如说链接10000次数据库(夸张一点啦....),并且这个类还不一定会被使用,那么这个创建过程就是无用的,怎么办呢?
为了解决这个问题,我们想到的新的解决方案:
public class SingletonClass { private static SingletonClass instance = null; public static SingletonClass getInstance() { if(instance == null) { instance = new SingletonClass(); } return instance; } private SingletonClass() { } }
代码的变化有俩处----首先,把 instance 设置为 null ,知道第一次使用的时候判是否为 null 来创建对象。因为创建对象不在声明处,所以那个 final 的修饰必须去掉。
我们来想象一下这个过程。要使用 SingletonClass ,调用 getInstance()方法,第一次的时候发现instance时null,然后就创建一个对象,返回出去;第二次再使用的时候,因为这个 instance事static的,共享一个对象变量的,所以instance的值已经不是null了,因此不会再创建对象,直接将其返回。
这个过程就称为lazy loaded ,也就是迟加载-----直到使用的时候才经行加载。
这样写法也比较完美:但是还可以优化
public class SingletonClass{ private static SingletonClass instance = null; public static SingletonClass getInstance(){ if(instance == null){ synchronized(SingletonClass.class){ if(instance == null){ instance = new SingletonClass(); } } } return instance; } private SingletonClass(){ } }
通过单例模式的学习告诉我们:
1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。
2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。
到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?
首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)
其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。
再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。
最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类 不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很 大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事 情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码:
还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:
[java] view plaincopy
1. public class Builder {
2.
3. private List<Sender> list = new ArrayList<Sender>();
4.
5. public void produceMailSender(int count){
6. for(int i=0; i<count; i++){
7. list.add(new MailSender());
8. }
9. }
10.
11. public void produceSmsSender(int count){
12. for(int i=0; i<count; i++){
13. list.add(new SmsSender());
14. }
15. }
16. }
测试类:
[java] view plaincopy
1. public class Test {
2.
3. public static void main(String[] args) {
4. Builder builder = new Builder();
5. builder.produceMailSender(10);
6. }
7. }
从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。在Java中,复制对象是通过clone()实现的,先创建一个原型类:
[java] view plaincopy
1. public class Prototype implements Cloneable {
2.
3. public Object clone() throws CloneNotSupportedException {
4. Prototype proto = (Prototype) super.clone();
5. return proto;
6. }
7. }
很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口 是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句 话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,我会 在另一篇文章中,关于解读Java中本地方法的调用,此处不再深究。在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
此处,写一个深浅复制的例子:
[java] view plaincopy
1. public class Prototype implements Cloneable, Serializable {
2.
3. private static final long serialVersionUID = 1L;
4. private String string;
5.
6. private SerializableObject obj;
7.
8. /* 浅复制 */
9. public Object clone() throws CloneNotSupportedException {
10. Prototype proto = (Prototype) super.clone();
11. return proto;
12. }
13.
14. /* 深复制 */
15. public Object deepClone() throws IOException, ClassNotFoundException {
16.
17. /* 写入当前对象的二进制流 */
18. ByteArrayOutputStream bos = new ByteArrayOutputStream();
19. ObjectOutputStream oos = new ObjectOutputStream(bos);
20. oos.writeObject(this);
21.
22. /* 读出二进制流产生的新对象 */
23. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
24. ObjectInputStream ois = new ObjectInputStream(bis);
25. return ois.readObject();
26. }
27.
28. public String getString() {
29. return string;
30. }
31.
32. public void setString(String string) {
33. this.string = string;
34. }
35.
36. public SerializableObject getObj() {
37. return obj;
38. }
39.
40. public void setObj(SerializableObject obj) {
41. this.obj = obj;
42. }
43.
44. }
45.
46. class SerializableObject implements Serializable {
47. private static final long serialVersionUID = 1L;
48. }
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,我们看下面的图:
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。首先,我们来看看类的适配器模式,先看类图:
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:
[java] view plaincopy
1. public class Source {
2.
3. public void method1() {
4. System.out.println("this is original method!");
5. }
6. }
[java] view plaincopy
1. public interface Targetable {
2.
3. /* 与原类中的方法相同 */
4. public void method1();
5.
6. /* 新类的方法 */
7. public void method2();
8. }
[java] view plaincopy
1. public class Adapter extends Source implements Targetable {
2.
3. @Override
4. public void method2() {
5. System.out.println("this is the targetable method!");
6. }
7. }
Adapter类继承Source类,实现Targetable接口,下面是测试类:
[java] view plaincopy
1. public class AdapterTest {
2.
3. public static void main(String[] args) {
4. Targetable target = new Adapter();
5. target.method1();
6. target.method2();
7. }
8. }
输出:
this is original method!
this is the targetable method!
这样Targetable接口的实现类就具有了Source类的功能
将之前介绍的所有排序算法整理成NumberSort类,代码
NumberSort
Java语言引入了Java虚拟机,具有跨平台运行的功能,能够很好地适应各种Web应用。同时,为了提高Java语言的性能和健壮性,还引入了如垃圾回收机制等新功能,通过这些改进让Java具有其独特的工作原理。
1.Java虚拟机
Java源程序通过编译器编译成.Class文件,然后java虚拟机中的java 解释器负责将字节码文件解释成为特定的机器码进行运行。
java是一种半编译半解释型语言。半编译是指:java源代码,会经过javac命令变成 .class文件。半解释是指: .class文件被jvm解释的过程。也就是因为jvm的半解释才有了java的动态语言特性:反射和annotation。
和android区别
alvik有自己的libdex库负责对.class进行处理。libdex主要对.class进行处理生成自己的dex文件。主要做的工作是,对虚拟机指令进行转换(dalvik是基于寄存器的,sun虚拟机是基于栈的),对类的静态数据进行归类、压缩。
dalvik基于寄存器,而JVM基于stack ,Dalvik执行的是特有的DEX文件格式,而JVM运行的是*.class文件格式。
优势:1、在编译时提前优化代码而不是等到运行时
2、 虚拟机很小,使用的空间也小;被设计来满足可高效运行多种虚拟机实例。
Java虚拟机的建立需要针对不同的软硬件平台来实现,既要考虑处理器的型号,也要考虑操作系统的种类。由此在SPARC结构、X86结构、MIPS和PPC等嵌入式处理芯片上,在UNIX、Linux、Windows和部分实时操作系统上都可实现Java虚拟机。
2.无用内存自动回收机制
而在Java运行环境中,始终存在着一个系统级的线程,专门跟踪内存的使用情况, 定期检测出不再使用的内存,并自动进行回收,避免了内存的泄露,也减轻了程序员的工作量。
1.JVM
JVM是Java平台的核心,为了让编译产生的字节码能更好地解释与执行,因此把JVM分成了6个部分:JVM解释器、指令系统、寄存器、栈、存储区和碎片回收区。
可以很明显看出,Android系统架构由5部分组成,分别是:Linux Kernel、Android Runtime、Libraries、Application Framework、Applications。第二部分将详细介绍这5个部分。
现在我们拿起手术刀来剖析各个部分。其实这部分SDK文档已经帮我们做得很好了,我们要做的就是拿来主义,然后再加上自己理解。下面自底向上分析各层。
Android基于Linux 2.6提供核心系统服务,例如:安全、内存管理、进程管理、网络堆栈、驱动模型。Linux Kernel也作为硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务。
Android 包含一个核心库的集合,提供大部分在Java编程语言核心类库中可用的功能。每一个Android应用程序是Dalvik虚拟机中的实例,运行在他们自己 的进程中。Dalvik虚拟机设计成,在一个设备可以高效地运行多个虚拟机。Dalvik虚拟机可执行文件格式是.dex,dex格式是专为Dalvik 设计的一种压缩格式,适合内存和处理器速度有限的系统。
大多数虚拟机包括JVM都是基于栈的,而Dalvik虚拟机则是基于寄存器的。 两种架构各有优劣,一般而言,基于栈的机器需要更多指令,而基于寄存器的机器指令更大。dx 是一套工具,可以將 Java .class 转换成 .dex 格式。一个dex文件通常会有多个.class。由于dex有時必须进行最佳化,会使文件大小增加1-4倍,以ODEX结尾。
Dalvik虚拟机依赖于Linux 内核提供基本功能,如线程和底层内存管理。
Android包含一个C/C++库的集合,供Android系统的各个组件使用。这些功能通过Android的应用程序框架(application framework)暴露给开发者。下面列出一些核心库:
· 系统C库——标准C系统库(libc)的BSD衍生,调整为基于嵌入式Linux设备
· 媒体库——基于PacketVideo的OpenCORE。这些库支持播放和录制许多流行的音频和视频格式,以及静态图像文件,包括MPEG4、 H.264、 MP3、 AAC、 AMR、JPG、 PNG
· 界面管理——管理访问显示子系统和无缝组合多个应用程序的二维和三维图形层
· LibWebCore——新式的Web浏览器引擎,驱动Android 浏览器和内嵌的web视图
· SGL——基本的2D图形引擎
· 3D库——基于OpenGL ES 1.0 APIs的实现。库使用硬件3D加速或包含高度优化的3D软件光栅
· FreeType ——位图和矢量字体渲染
· SQLite ——所有应用程序都可以使用的强大而轻量级的关系数据库引擎
通过提供开放的开发平台,Android使开发者能够编制极其丰富和新颖的应用程序。开发者可以自由地利用设备硬件优势、访问位置信息、运行后台服务、设置闹钟、向状态栏添加通知等等,很多很多。
开发者可以完全使用核心应用程序所使用的框架APIs。应用程序的体系结构旨在简化组件的重用,任何应用程序都能发布他的功能且任何其他应用程序可以使用这些功能(需要服从框架执行的安全限制)。这一机制允许用户替换组件。
所有的应用程序其实是一组服务和系统,包括:
· 视图(View)——丰富的、可扩展的视图集合,可用于构建一个应用程序。包括包括列表、网格、文本框、按钮,甚至是内嵌的网页浏览器
· 内容提供者(Content Providers)——使应用程序能访问其他应用程序(如通讯录)的数据,或共享自己的数据
· 资源管理器(Resource Manager)——提供访问非代码资源,如本地化字符串、图形和布局文件
· 通知管理器(Notification Manager)——使所有的应用程序能够在状态栏显示自定义警告
· 活动管理器(Activity Manager)——管理应用程序生命周期,提供通用的导航回退功能
Android装配一个核心应用程序集合,包括电子邮件客户端、SMS程序、日历、地图、浏览器、联系人和其他设置。所有应用程序都是用Java编程语言写的。更加丰富的应用程序有待我们去开发!
一、Socket通信简介
Android 与服务器的通信方式主要有两种,一是Http通信,一是Socket通信。两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立 连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据。而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可 实现信息的主动推送,而不需要每次由客户端想服务器发送请求。 那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信。通过建立socket连接,可为通信双方的数据传输 传提供通道。socket的主要特点有数据丢失率低,使用简单且易于移植。
1.2Socket的分类
根据不同的的底层协议,Socket的实现是多样化的。本指南中只介绍TCP/IP协议族的内容,在这个协议族当中主要的Socket类型为流套接字 (streamsocket)和数据报套接字(datagramsocket)。流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。数据 报套接字使用UDP协议,提供数据打包发送服务。
二、Socket 基本通信模型
三、Socket基本实现原理
3.1基于TCP协议的Socket
服务器端首先声明一个ServerSocket对象并且指定端口号,然后调 用Serversocket的accept()方法接收客户端的数据。accept()方法在没有数据进行接收的处于堵塞状态。 (Socketsocket=serversocket.accept()),一旦接收到数据,通过inputstream读取接收的数据。
客户端创建一个Socket对象,指定服务器端的ip地址和端口号 (Socketsocket=newSocket("172.168.10.108",8080);),通过inputstream读取数据,获取服务器 发出的数据(OutputStreamoutputstream=socket.getOutputStream()),最后将要发送的数据写入到 outputstream即可进行TCP协议的socket数据传输。
3.2基于UDP协议的数据传输
服务器端首先创建一个DatagramSocket对象,并且指点监听的端 口。接下来创建一个空的DatagramSocket对象用于接收数据 (bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)), 使用DatagramSocket的receive方法接收客户端发送的数据,receive()与serversocket的accepet()类似, 在没有数据进行接收的处于堵塞状态。
客户端也创建个DatagramSocket对象,并且指点监听的端口。接 下来创建一个InetAddress对象,这个对象类似与一个网络的发送地址 (InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定义要发送的 一个字符串,创建一个DatagramPacket对象,并制定要讲这个数据报包发送到网络的那个地址以及端口号,最后使用DatagramSocket 的对象的send()发送数据。*(Stringstr="hello";bytedata[]=str.getByte(); DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)
四、android 实现socket简单通信
4.1使用TCP协议通信
android端实现:
[java] view plaincopy
1. protected void connectServerWithTCPSocket() {
2.
3. Socket socket;
4. try {// 创建一个Socket对象,并指定服务端的IP及端口号
5. socket = new Socket("192.168.1.32", 1989);
6. // 创建一个InputStream用户读取要发送的文件。
7. InputStream inputStream = new FileInputStream("e://a.txt");
8. // 获取Socket的OutputStream对象用于发送数据。
9. OutputStream outputStream = socket.getOutputStream();
10. // 创建一个byte类型的buffer字节数组,用于存放读取的本地文件
11. byte buffer[] = new byte[4 * 1024];
12. int temp = 0;
13. // 循环读取文件
14. while ((temp = inputStream.read(buffer)) != -1) {
15. // 把数据写入到OuputStream对象中
16. outputStream.write(buffer, 0, temp);
17. }
18. // 发送读取的数据到服务端
19. outputStream.flush();
20.
21. /** 或创建一个报文,使用BufferedWriter写入,看你的需求 **/
22. // String socketData = "[2143213;21343fjks;213]";
23. // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
24. // socket.getOutputStream()));
25. // writer.write(socketData.replace("\n", " ") + "\n");
26. // writer.flush();
27. /************************************************/
28. } catch (UnknownHostException e) {
29. e.printStackTrace();
30. } catch (IOException e) {
31. e.printStackTrace();
32. }
33.
34. }
服务器端简单实现:
[java] view plaincopy
1. public void ServerReceviedByTcp() {
2. // 声明一个ServerSocket对象
3. ServerSocket serverSocket = null;
4. try {
5. // 创建一个ServerSocket对象,并让这个Socket在1989端口监听
6. serverSocket = new ServerSocket(1989);
7. // 调用ServerSocket的accept()方法,接受客户端所发送的请求,
8. // 如果客户端没有发送数据,那么该线程就停滞不继续
9. Socket socket = serverSocket.accept();
10. // 从Socket当中得到InputStream对象
11. InputStream inputStream = socket.getInputStream();
12. byte buffer[] = new byte[1024 * 4];
13. int temp = 0;
14. // 从InputStream当中读取客户端所发送的数据
15. while ((temp = inputStream.read(buffer)) != -1) {
16. System.out.println(new String(buffer, 0, temp));
17. }
18. serverSocket.close();
19. } catch (IOException e) {
20. e.printStackTrace();
21. }
22. }
4.2使用UDP协议通信
客户端发送数据实现:
[java] view plaincopy
1. protected void connectServerWithUDPSocket() {
2.
3. DatagramSocket socket;
4. try {
5. //创建DatagramSocket对象并指定一个端口号,注意,如果客户端需要接收服务器的返回数据,
6. //还需要使用这个端口号来receive,所以一定要记住
7. socket = new DatagramSocket(1985);
8. //使用InetAddress(Inet4Address).getByName把IP地址转换为网络地址
9. InetAddress serverAddress = InetAddress.getByName("192.168.1.32");
10. //Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.1.32");
11. String str = "[2143213;21343fjks;213]";//设置要发送的报文
12. byte data[] = str.getBytes();//把字符串str字符串转换为字节数组
13. //创建一个DatagramPacket对象,用于发送数据。
14. //参数一:要发送的数据 参数二:数据的长度 参数三:服务端的网络地址 参数四:服务器端端口号
15. DatagramPacket packet = new DatagramPacket(data, data.length ,serverAddress ,10025);
16. socket.send(packet);//把数据发送到服务端。
17. } catch (SocketException e) {
18. e.printStackTrace();
19. } catch (UnknownHostException e) {
20. e.printStackTrace();
21. } catch (IOException e) {
22. e.printStackTrace();
23. }
24. }
客户端接收服务器返回的数据:
[java] view plaincopy
1. public void ReceiveServerSocketData() {
2. DatagramSocket socket;
3. try {
4. //实例化的端口号要和发送时的socket一致,否则收不到data
5. socket = new DatagramSocket(1985);
6. byte data[] = new byte[4 * 1024];
7. //参数一:要接受的data 参数二:data的长度
8. DatagramPacket packet = new DatagramPacket(data, data.length);
9. socket.receive(packet);
10. //把接收到的data转换为String字符串
11. String result = new String(packet.getData(), packet.getOffset(),
12. packet.getLength());
13. socket.close();//不使用了记得要关闭
14. System.out.println("the number of reveived Socket is :" + flag
15. + "udpData:" + result);
16. } catch (SocketException e) {
17. e.printStackTrace();
18. } catch (IOException e) {
19. e.printStackTrace();
20. }
21. }
服务器接收客户端实现:
[java] view plaincopy
1. public void ServerReceviedByUdp(){
2. //创建一个DatagramSocket对象,并指定监听端口。(UDP使用DatagramSocket)
3. DatagramSocket socket;
4. try {
5. socket = new DatagramSocket(10025);
6. //创建一个byte类型的数组,用于存放接收到得数据
7. byte data[] = new byte[4*1024];
8. //创建一个DatagramPacket对象,并指定DatagramPacket对象的大小
9. DatagramPacket packet = new DatagramPacket(data,data.length);
10. //读取接收到得数据
11. socket.receive(packet);
12. //把客户端发送的数据转换为字符串。
13. //使用三个参数的String方法。参数一:数据包 参数二:起始位置 参数三:数据包长
14. String result = new String(packet.getData(),packet.getOffset() ,packet.getLength());
15. } catch (SocketException e) {
16. e.printStackTrace();
17. } catch (IOException e) {
18. e.printStackTrace();
19. }
20. }
五、总结:
使用UDP方式android端和服务器端接收可以看出,其实android端和服务器端的发送和接收大庭相径,只要端口号正确了,相互通信就没有问题,TCP使用的是流的方式发送,UDP是以包的形式发送。
本示例以Servlet为例,演示Android与Servlet的通信。
众所周知,Android与服务器通信通常采用HTTP通信方式和Socket通信方式,而HTTP通信方式又分get和post两种方式。至于Socket通信会在以后的博文中介绍。
HTTP协议简介:
HTTP (Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种协议。
HTTP连接最显 著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。 在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
由 于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”、“无状态”,要保持客户端程序的在线状态,需要不断地向服务器发起连接 请求。通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回 复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
基于HTTP1.0协议的客户端在每次向服务器发出请求后,服务器就会向客户端返回响应消息,在确认客户端已经收到响应消息后,服务端就会关闭网络连接。在这个数据传输过程中,并不保存任何历史信息和状态信息,因此,HTTP协议也被认为是无状态的协议。
HTTP1.1 和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持。当客户端使用HTTP1.1协议连接到服务器后,服务器就将关闭客户端连接的主动权交还 给客户端;也就是说,只要不调用Socket类的close方法关闭网络连接,就可以继续向服务器发送HTTP请求。
HTTP连接使用的是“请求—响应”的方式(2次握手),不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。而Socket连接在双方建立起连接后就可以直接进行数据的传输
HTTP协议的特点:
支持B/S及C/S模式;
简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由Content-Type 加以标记;
无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
HTTP协议请求方法:
请求行中包括了请求方法,解释如下:
GET 请求获取Request-URI 所标识的资源;
POST 在Request-URI 所标识的资源后附加新的数据;
HEAD 请求获取由Request-URI 所标识的资源的响应消息报头
PUT 请求服务器存储一个资源,并用Request-URI 作为其标识
DELETE 请求服务器删除Request-URI 所标识的资源;
TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT 保留将来使用
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求
Get与Post请求区别:
Post 请求可以向服务器传送数据,而且数据放在HTML HEADER内一起传送到服务端URL地址,数据对用户不可见。而get是把参数数据队列加到提交的URL中,值和表单内各个字段一一对应, 例如(http://www.baidu.com/s?w=%C4&inputT=2710)
get 传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
get安全性非常低,post安全性较高。
在Android开发中我们经常会用到网络连接功能与服务器进行数据的交互,为此Android的SDK提供了Apache的HttpClient来方便我们使用各种Http服务
///
Get() 先创建一个HttpClient 然后再创建一个HttpGet,通过HttpClient的execute方法来发送一个HttpGet并且返回String内容。
try {
// 创建一个默认的HttpClient
HttpClient httpclient =new DefaultHttpClient();
// 创建一个GET请求
HttpGet request =new HttpGet("www.google.com");
// 发送GET请求,并将响应内容转换成字符串
String response = httpclient.execute(request, new BasicResponseHandler());
Log.v("response text", response);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
///////////////////////////////
Post()
publicstatic String post(String url, NameValuePair... params) {
try {
// 编码参数
List<NameValuePair> formparams =new ArrayList<NameValuePair>(); // 请求参数
for (NameValuePair p : params) {
formparams.add(p);
}
UrlEncodedFormEntity entity =new UrlEncodedFormEntity(formparams,
CHARSET);
// 创建POST请求
HttpPost request =new HttpPost(url);
request.setEntity(entity);
// 发送请求
HttpClient client = getHttpClient();
HttpResponse response = client.execute(request);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
thrownew RuntimeException("请求失败");
}
HttpEntity resEntity = response.getEntity();
return (resEntity ==null) ?null : EntityUtils.toString(resEntity, CHARSET);
} catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage());
returnnull;
} catch (ClientProtocolException e) {
Log.w(TAG, e.getMessage());
returnnull;
} catch (IOException e) {
thrownew RuntimeException("连接失败", e);
}
}
整理:杨光福&王飞龙
时间仓促,难免有不周之处,请见谅。
http://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html
可以用我们上课讲的说
也可以参照
http://blog.saymagic.cn/2015/01/30/android-pic-three-cache.html
http://www.cnblogs.com/elliotta/p/3633752.html
http://blog.csdn.net/yudajun/article/details/9323941
技术方面说明
http://blog.csdn.net/lwyygydx/article/details/41870377
功能改进方面说明
http://digi.tech.qq.com/a/20150121/012030.htm
可以参照李延磊老师的
也可以参照链接
http://blog.csdn.net/fancylovejava/article/details/39927539
xUtils,Gson 极光推送 都讲过,忽略
友盟第三方登录
http://blog.umeng.com/uncategorized/4160.html
第三方登录案例
http://blog.csdn.net/yueqinglkong/article/details/15028041
http://blog.csdn.net/lyf_007217/article/details/8542238
http://www.cnblogs.com/devinzhang/p/3856200.html
http://www.360doc.com/content/14/0402/09/10504424_365635496.shtml
http://blog.csdn.net/androidzhaoxiaogang/article/details/7910364
http://www.2cto.com/kf/201409/335964.html
http://blog.csdn.net/u200814499/article/details/40391443
http://blog.csdn.net/jiangliloveyou/article/details/9849775
http://blog.csdn.net/lnb333666/article/details/7471292
http://skywen.iteye.com/blog/1811310
已经讲了,请看视频
已经讲了,请看视频
已经讲了,请看视频
http://blog.csdn.net/wuqiong_524itcast/article/details/25378685
http://blog.csdn.net/wangshione/article/details/8490245
http://blog.csdn.net/lnb333666/article/details/8031770
1.垃圾收集算法的核心思想
Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则 为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。
2.触发主GC(Garbage Collector)的条件
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以 便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
3.减少GC开销的措施
根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:
(1)不要显式调用System.gc()
此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。
(2)尽量减少临时对象的使用
临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
(3)对象不用时最好显式置为Null
一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。
(4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)
由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如 Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但 这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。
(5)能用基本类型如Int,Long,就不用Integer,Long对象
基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。
(6)尽量少用静态对象变量
静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
(7)分散对象创建或删除的时间
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片, 从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC 的机会。
gc()函数的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。在Davilk中,给程序分配的内存是根据机型厂商的不同而不同(现在大部分为32MB),在VM内部会将内存分为:java使用的内存,Native使用的内存,他们之间不能共享,当某一方面不足
的时候必须向VM申请,而不能直接使用另外一个的内存。
出现内存泄漏的可能性:
出现情况:
1. 数据库的cursor没有关闭
2.构造adapter时,没有使用缓存contentview
衍生listview的优化问题-----减少创建view的对象,充分使用contentview,可以使用一静态类来优化处理getview的过程
3.Bitmap对象不使用时采用recycle()释放内存
4.activity中的对象的生命周期大于activity
调试方法: DDMS==> HEAPSZIE==>dataobject==>[Total Size]
一、 Android的内存机制
Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java 堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清 洁工(GC)
二、GC是什么? 为什么要有GC?
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以 自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
四、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对 象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。可以。程序员可 以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对 象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象 数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。
Java带垃圾回收的机制,为什么还会内存泄露呢?举例:
[java] view plaincopyprint?
1. Vector v = new Vector(10);
2. for (int i = 1; i < 100; i++) {
3. Object o = new Object();
4. v.add(o);
5. o = null;
6. }// 此时,所有的Object对象都没有被释放,因为变量v引用这些对象。
Java 内存泄露的根本原因就是 保存了不可能再被访问的变量类型的引用
六、Android的内存溢出
Android的内存溢出是如何发生的?
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。也就是说我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。
为什么会出现内存不够用的情况呢?我想原因主要有两个:
由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。
(一)、(二)中,我们了解了一些基本概念。
600dp的含义是:代表这个设备的最短的那一边。
获取设备的最短边的代码是:Configuration config = getResources().getConfiguration();
int smallestScreenWidth = config.smallestScreenWidthDp;
这个时候拿smallestScreenWidth 与600想比较就可以知道该设备能否读取里面的资源了。
)
除此之外,为了方便适配,在编码时我们还应该注意什么呢,主要有以下几点:
(1)多使用权重(android:layout_weight)
尤其是在tab切换布局,listview title及Item布局等情况下;
(2)设置宽度和高度时,尽量使用match_parent和wrap_content,避免把控件宽高设死;
(3)父容器布局选用
多使用RelativeLayout,FrameLayout,GridLayout等,减少布局层次。当然,在使用
权重时,得采用LinearLayout;
(4) 在xml里,设置高度、宽度采用dp(dip),设置字体采用sp。
(应该注意,在代码里面,我们写的setHeight(...)单位是px)
(二)
那么在具体开发中,我们应该注意什么呢。
首先,我们必须要知道,其实适配的关键在于两点:
(1)不同分辨率设备的适配,这点在单位的使用上用dp、sp以及图片资源存放于不同的drawable文件夹就可以解决问题;
(2)不同尺寸的适配,这点主要靠将相关值以及布局文件放置于不同的文件夹中来解决。
2.1 values文件夹
可以在工程下创建不同的values文件夹:values-sw480dp, values-sw600dp,
values-sw720dp-land等。比如一个控件的宽度,在10寸pad上是10dp,在8寸pad
上是5dp。这时,你可以定义一个变量,button_width,然后在values-sw600dp
下写5dp,在values-sw720-land下写
10dp。这样就达到了在不同尺寸pad上,
相应控件大小不一样的效果。
2.1 layout文件夹
如果在不同尺寸设备上展示的布局有明显差别,仅仅用values不同已经难以控制,
那么就可以考虑写不同的布局文件置于不同的layout文件夹下,android会根据设备
尺寸去加载相应文件夹下的布局文件。如:layout-sw480dp,layout-sw600dp,
layout-sw700dp等。
值得注意的是,如果不是很有必要,尽量采用2.1方案,方便维护。如果尺寸和分辨率都不同,
那么就要结合(1)、(2)考虑了。
(补充:其实values文件夹和layout文件夹不仅仅是根据尺寸判断,也和分辨率有关,不过在通常情况下,
综合计算考虑,仅根据尺寸判断就可以了: