----------------------------------------------Java解惑系列----------------------------------------------------
(一)类的初始化顺序:
1.父类--静态变量
2.父类--静态初始化块
3.子类--静态变量
4.子类--静态初始化块
5.父类--变量
6.父类--初始化块
7.父类--构造器
8.子类--变量
9.子类--初始化块
10.子类--构造器
【注:静态变量和静态初始化块是依照他们在类中定义的顺序进行初始化的;同样变量和初始化块也遵循这个规律】
(二)创建了几个String对象
*常用的创建一个类的实例(对象)的方法有两种
1.使用new创建对象
2.调用Class类的newInstance方法,利用反射机制创建对象
但String特有的另一种创建对象的方式
3.引号内包括文本
String str="abc"
Java虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,如果已存在则不创建新的对象,直接返回已存在对象的
引用;如果没有则先创建这个对象,然后把它加入到字符串池中,再将他的引用返回。
【字符串常量池中对象的共享能够带来效率的提高,因此提倡大家用引号包括文本的方式来创建String对象;
常量的值在编译的时候就会被确定;】
*"=="可以用来比较两个变量
1.如果比较的是两个基本类型(char,byte,short,int,long,float,boolean,double),则是判断他们的值是否相等;
2.如果比较的是两个对象变量,则是判断他们的引用是否指向同一个对象;
(三)变量的覆盖
变量的值取决于我们定义的变量的类型,而不是创建的对象的类型;
【当变量类型是父类时,不管我们创建的对象是父类还是子类的,都不存在属性覆盖的问题;】
1.由于private受访问权限的限制,它不能被覆盖;
2.属性的值取父类还是子类的,并不取决于我们对象的类型(实际类型),而是取决于定义的变量的类型(静态类型);
3.friendly,protected,public修饰符并不影响属性的覆盖;
4.静态变量和静态常量属于类,因此它们不能被覆盖;
(四)final,finally,finalize的区别
final主要用于4个地方:
1.定义变量,包括静态的和非静态的;———— 如果修饰基本类型,表示这个变量被赋予的值是不可变的,即他是个常量;
如果修饰的是一个对象,表示这个变量被赋予的引用是不可变的;
2.定义方法的参数;
【综合1,2,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次】
*final变量和静态final变量未被初始时,编译会报错;
*用final修饰的变量比非fianl的变量拥有更高的效率,因此实际编程中应尽多的用常量来代替普通变量;
被final修饰的变量必须被初始化,初始化的方式有以下几种:
①在定义的时候初始化
②final变量可以在初始化块中初始化,不可以在静态初始化块中初始化
③静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始化
④final变量可以在类的构造器中初始化,但是静态final变量不可以
3.定义方法;———— 表示这个方法不可以被子类重写,但是它不影响它被子类继承;
4.定义类;———— final类不允许被继承,因此编译器在处理时把它所有的方法都当做final的,因此final类比普通类拥有更高的效率;
final类的方法不能被重写,因此不能用来修饰接口;
final类的方法不能被重写,但是final类的属性(变量)值是可变的,除非增加final属性,使属性值不可改变;
finally语句:
用在try/catch语句中,附带着一个语句块,表示这段语句最终总是会被执行;
1.return,continue,break语句都不能阻止finally语句块的执行;
2.finally语句块是在return方法退出之前,或循环被continue跳过,或循环被中断之前执行的;
finalize方法:
1.finalize()方法是一个属于object的方法,finalize()方法是GC垃圾回收运行机制的一部分;
2.finalize()方法是在GC清理它所从属的对象时被调用的,如果执行他的过程中出现了无法捕获的异常,GC将终止对该对象的清理,并且
该异常会被忽略;直到下一次GC开始清理这个对象时,他的finalize()会被再次调用;
3.system.gc()只是建议垃圾收集器GC启动,清理无用的对象释放内存空间,但是GC的启动是不一定的,由JAVA虚拟机来决定。
直到JAVA虚拟机停止运行时,有些对象的finalize()可能都没有运行过;
4.finalize()方法属于object类,object的任意子类都可以重写该方法,在其中释放系统资源或做其他的清理工作,如关闭输入输出流;
(五)传值还是传引用
JAVA中变量有以下两种:
1.基本类型变量,char, byte, short, int ,long, float, double, boolean;
2.引用类型变量,包括类,接口,数组(基本类型数组和对象数组);
*当基本类型的变量被当做参数传递给方法时,java虚拟机所做的工作是把这个值拷贝了一份,然后把拷贝的值传递到了方法的内部;
*当引用类型变量被当做参数传递给方法时,java虚拟机会拷贝一份这个变量所持有的引用,然后把它传递给java虚拟机为方法创建的局
部变量,从而这两个变量指向了同一个对象;
结论:
1.基本类型和基本类型变量被当做参数传递给方法时,是值传递,在方法实体中,无法给原变量再重新赋值,也无法改变他的值;
2.对象和引用型变量被当做参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它所指向对象的属性;
(六)字符串String杂谈
1.String的length()方法和数组的length属性;
2.中文汉字在char中的保存:
①java中一个char是两个字节,一个中文汉字是一个字符,GBK编码汉字是2个字节,UTF-8编码3个字节,所以通常来说,GBK编码char是可以保存汉字的;
一个英文字母是一个字节的,因此能够保存到一个byte,但是一个中文汉字不能保存到byte;
②字符串之间的“+”,效果是字符串的拼接;
字符之间的“+” ,效果等同于数字和数字之间,是一种数学运算;
3.字符串的反转输出
①取长度,for循环递减,反序得到charAt(i),连接字符串;
②new StringBuffer,然后使用StringBuffer的reverse自带函数反转;
4.按字节截取含有中文的字符串
①不同编码的中文字符,占有的字节数不同;GBK编码汉字2个字节,UTF-8编码汉字3个字节,UTF-16编码汉字4个字节;
②汉字截取了一半则无法正常显示,所以必须判断字符是否为汉字;可以根据如果当前字符的字节数>1,则说明字符为汉字;
因为所有编码的英文字符,占有1个字节;
③charAt(int index)是按照字符来分解字符串的;
substring(int beginIndex, int endIndex) 也是按字符截取的
(七)日期和时间的处理
(略)
(八)基本类型
1.基本类型都有对应的包装类,分为字符型,布尔类型,数值类型;
2.数值类型分为整数型byte,short,int,long 和浮点数类型float,double;
3.Java中的数值类型取值范围是固定的,不随机器硬件环境或操作系统改变,对于数值类型的取值范围,无需强制记忆,因为他们的值都以常量
的形式定义在对应的包装类中;
如 byte二进制位数:Byte.SIZE
byte最小值 :Byte.MIN_VALUE=-128
byte最大值 :Byte.MAX_VALUE=127
4.基本类型存储在栈中,因此它们的存储速度快于存储在堆中的对应包装类的实例对象;
几点结论:
①未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为double类型;
②如果一个整数的值超出了int类型能够表示的范围,则必须增加后缀"L",表示为long型;
③带有"F"后缀的整数和浮点数都是float类型的;带有"D"后缀的整数和浮点数都是double类型的;
④编译器会在编译期对byte,short,int,long,double,char型变量的值进行检查,如果超出了它们的取值范围就会报错;
⑤int型值可以赋给所有数值类型的变量;
long型值可以赋给long,float,double类型的变量;
float型值可以覆盖float,double类型的变量;
double型值只能赋给double类型的变量;
5.运算符对基本类型的影响
当使用+,-,*,/,%运算符对基本类型运算时,遵循如下规则
①只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,结果也是double类型;
②否则只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,结果也是float类型;
③否则只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,结果也是long类型;
当使用+=,-=,*=,/=,%=运算符对基本类型运算时,遵循如下规则
①运算符右边的数值首先被强制转换成与运算符左边数值相同的类型,然后再执行运算,且运算结果与运算符左边数值类型相同;
当使用==运算符在基本类型和其包装类对象之间比较时,遵循如下规则
①只要两个操作数中有一个是基本类型,就是比较他们的数值是否相等;
②否则就是判断这两个对象的内存地址是否相等,及是否是同一个对象;
6.Math.round()方法
7.switch语句
①byte,char,short,int四种基本类型以及他们的包装类都可以用于switch语句;
②long,float,double,boolean四种基本类型及他们的包装类都不能用于switch语句;
③enum枚举类型,可以用于switch语句,但是要在java 5.0(jdk 1.5)以上的版本才支持;
④所有类型的对象(包括String类,但在java 5.0/1.5以上版本中,排除byte,short,int,char四种基本类型对应的包装类)
都不能用于switch语句;
(九)继承、多态、重载、重写
继承和组合
①.继承给我们带来的好处就是对原有类的复用;类的复用可以提高我们的开发效率;
②.组合就是把原有的类定义为新类的一个属性,通过在新类中调用原有类的方法来实现复用;
③.使用继承和组合复用原有的类,都是一种增量式的开发模式,即不需要修改原有的代码;可以规避对原有代码修改而带来的风险;
多态
①多态是一种类型(都是car类型)表现出多种状态(宝马汽车状态BMW,奇瑞汽车状态cherryQQ)。
②方法的调用同方法所属的主题关联起来叫做绑定,分为前期绑定和后期绑定;
前期绑定:由编译器和连接程序实现,在程序运行之前进行绑定,又叫静态绑定;
后期绑定:由方法调用机制实现,在运行时根据对象的类型进行绑定,又叫动态绑定或运行时绑定;
③多态就是在后期绑定的机制上实现的,多态好处是消除类之间的耦合,使程序更容易扩展;
重载和重写
(略)
(十)多线程
*实现线程的两种方法
①继承Thread类,实现run()方法,将线程的执行主体放入其中;
②实现Runnable接口,实现run()方法,将线程的执行主体放入其中;
线程池:
①自己实现类:启动、停止、执行任务、关联任务队列等;
②java 5.0以上自己实现了线程池功能
————newCachedThreadPool创建动态线程池,线程数量会根据需要创建和回收;适合于执行短期大量任务;
————newFixedThreadPool创建包含固定数量线程对象的线程池;
————newSingleThreadExecutor在线程池中创建一个线程来执行所有任务;
三个方法都返回了一个ExecutorService类型的对象;该对象有两个方法submit(),shutdown()
————submit 接受任务,交于线程池中的线程去运行;
参数Runnable接口,实现run方法,无返回值;适合做操作,不关心结果;
参数Callable接口,实现call方法,call()将任务的执行结果作为返回值;适合于执行某种操作后,需要知道执行结果;
————shutdown 停止服务,否则所有线程会保持运行;
(十一)运算符
(略)
----------------------------------------------Java系列问题----------------------------------------------------
1.父类引用指向子类对象(Java中的多态)
class Father {
void print() { System.out.println("Father print"); }
int a=10;
}
class Son extends Father {
void print() { System.out.println("Son print"); }
void show() { System.out.println("Son show"); }
int a=20;
}
class Demo {
public static void main(String args[]) {
Father obj = new Son();
obj.print(); // 调用的是 Son print
obj.show(); // 这个调用会报错!
obj.a; // 调用的是Father中的a,值为10
}
}
Father obj = new Son(); 编译看左边(静态类型)
运行看右边(实际类型)
obj.print(); // 调用的是 Son print * 方法的接收者以实际类型为判定依据
obj.a; // 调用的是Father中的a,值为10 * 变量的值以静态类型为判定依据
* 因为多态是针对方法的重写
1.当出现“父类引用指向子类对象”的情况时,如果子类中重写了父类中的一个方法,那么父类引用在调用这个方法的时候,将会调用子类中
的这个方法(动态绑定、执行);
2.如果你想实现多态,那么必须有三个条件:父类引用,子类对象,方法覆盖;
这里,如果Fathor类有一个show()方法,那么形成方法覆盖,此时才可以这么写:obj.show()——此刻形成了多态。
3.在没有方法覆盖的情况下,用父类引用去访问一个子类的方法,如上面的show()方法——由于父类引用没有这么大范围的权限,所以会报错;
4.父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的(对于子类中的成员变
量,它也是无法调用的);另外,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
【定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。】
5.静态方法是没有多态性的:静态方法不会被子类所覆盖,因此,父类引用只能调用父类的静态方法。
附:一些问题的解答
A:当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员
Q:为什么这样不可以?因为f是FatherClass,所以编译器只知道f拥有FatherClass.class的信息,FatherClass.class以外的信息,编译器
不知道,而子类的对象成员是在SonClass.class里,也就是说在FatherClass.class以外,所以f无法访问子类的对象成员。
2.Java的异常和错误
Java异常可分为3种:
(1)编译时异常:Java.lang.Exception
(2)运行期异常:Java.lang.RuntimeException
(3)错误:Java.lang.Error
* 编译时异常指的是我们必须在代码中显示的处理,或者try或者throw,处理完成后才能编译成功,常见的是IOException, InterruptedException。
* 运行期异常指的是我们写的代码可以编译通过,但是如果运行时出现问题,则会出现运行期异常,最常见的就是NullPointerException、
IndexOutOfBoundsException、ClassNotFound、ResourceNotFound,0被除,这类异常需要更改程序来避免。
* 运行时异常也可以通过捕捉预处理,因为运行时异常如果足够仔细,代码写的足够健壮,完全可以避免;
* Java.lang.Exception和Java.lang.Error继承自Java.lang.Throwable;
Java.lang.RuntimeException继承自Java.lang.Exception.
* Error是错误,可能源于程序的bug,但一般更可能源于环境问题, 出了Error程序就挂了,最常见的就是OutOfMemoryError。
3.编码问题(UTF-8、gb2312、unicode)
A:字符集(CharacterSet)?
Q:字面上的理解就是字符的集合,例如ASCII字符集,定义了128个字符;GB2312定义了7445个字符。而计算机系统中提到的字符集准确来说,指
的是已编号的字符的有序集合(不一定是连续)。
Q:Unicode是两个字节吗?
A:Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储为什么样的字节流,取决于字符编码方案。
推荐的Unicode编码是UTF-16和UTF-8。
Q:带签名的UTF-8指的是什么意思?
A:带签名指的是字节流以BOM标记开始。很多软件会“智能”的探测当前字节流使用的字符编码,这种探测过程出于效率考虑,通常会提取字节流前
面若干个字节,看看是否符合某些常见字符编码的编码规则。由于UTF-8和ASCII编码对于纯英文的编码是一样的,无法区分开来,因此通过在
字节流最前面添加BOM标记可以告诉软件,当前使用的是Unicode编码,判别成功率就十分准确了。但是需要注意,不是所有软件或者程序都能
正确处理BOM标记,例如PHP就不会检测BOM标记,直接把它当普通字节流解析了。因此如果你的PHP文件是采用带BOM标记的UTF-8进行编码的,
那么有可能会出现问题。
Q:Unicode编码和以前的字符集编码有什么区别?
A:早期字符编码、字符集和代码页等概念都是表达同一个意思。例如GB2312字符集、GB2312编码,936代码页,实际上说的是同个东西。但是对于
Unicode则不同,Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码,则是对UTF-8、UCS-2/UTF-16等具体编码方案的统称而已,
并不是具体的编码方案。所以当需要用到字符编码的时候,你可以写gb2312,codepage936,utf-8,utf-16,但请不要写unicode(看过别人
在网页的meta标签里头写charset=unicode,有感而发)。
(1)GB2312以及GBK字符集,限定了使用最多2个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查表,也就是通过代码页就可
以直接将字符映射为存储设备上的字节流了
(2)“虽然每个字符在Unicode字符集中都能找到唯一确定的编号(字符码,又称Unicode码),但是决定最终字节流的却是具体的字符编码”
(3)unicode是包含了gb2312中的字符集,但是每种编码解码方式不一样,也就是说,utf-8编码是一种规则,他自己编码的字节流,需要用自己的规则
去解码,utf-8是变长编码的,它不能按照gb2312那样解析字节流。所以如果不一致,就会出现乱码情况
4.static作用?
①可修饰变量(属性);
————Static int data 语句说明data为类变量,为一个类的共享变量,属于整个类。
②可修饰代码块
————当static修饰代码块时(注:此代码块要在此类的任何一个方法之外)那么这个代码块在代码被装载进虚拟机生成对象的时候可被装载
一次,以后再也不执行了。一般静态代码块被用来初始化静态成员。
③可修饰方法;
————Public static void printData(){}
表明此类方法为类方法(静态方法)。静态方法不需要有对象,可以使用类名调用。静态方法中不允许访问类的非静态成员,包括成
员的变量和方法,因为此时是通过类调用的,没有对象的概念。This.data是不可用的。
一般情况下,主方法是静态方法,所以可调用静态方法,主方法为静态方法是因为它是整个软件系统的入口,而进入入口时系统中没
有任何对象,只能使用类调用。
5.JAVA类加载机制
使用使用java编译器可以把JAVA代码编译为存储字节码的Class文件,Class文件是二进制字节流;
虚拟机不关心Class的来源什么,只要符合Class文件的结构就可以在java虚拟机中运行;即语言无关性;如JRuby\Jython等都可以
编译出Class字节码文件,在JAVA虚拟机上运行;
Class文件的结构:二进制流,各项数据紧凑排列,如魔数(头4个字节,文件格式),版本号(5~6字节),常量池(字面量、符号
引用),访问标志等;
JDK的bin目录下使用专门用于分析Class文件字节码的工具:javap,可以查看编译器生成的字节码;
总之,Class文件是Java虚拟机执行引擎的数据入口;Class文件中描述的各种信息,最终都需要加载到虚拟机中才能被运行和使用;
虚拟机把Class文件加载到内存、进行校验、转换解析和初始化,最终形成被虚拟机直接使用的Java类型,这就是虚拟机的加载机制;
动态加载的机制,提供大家一个方法: 在命令行窗口运行Java程序的时候,加上这个很有用的参数:
java -verbose *.class
这样会清晰的打印出被加载的类文件,大部分是jdk自身运行需要的,最后几行会明显的看到自己用到的那几个类文件被加载进来的顺序。即
使你声明了一个类对象,不实例化也不会加载,说明只有真正用到那个类的实例即对象的时候,才会执行加载。
虚拟机中类加载器:通过一个类的全限定名来获取描述此类的二进制字节流,及class文件;
虚拟机执行的步骤:
1.虚拟机有一个用于类加载的机制,用于从数据源中读取类文件,这个源可以是磁盘文件或者web上的文件,这里假设要加载的是TestLoader。
2.如果TestLoader这个类拥有类型为其他类的实例或者是某个类的子类,也就是说它和其他类产生关联或者依赖,那么相关的类也会被加载
3.虚拟机执行TestLoader中的main方法
4.如果main方法中有对其他类的依赖,那么加载相应的类。
可见联系虚拟机和我们的程序的“中间人”就是类加载器,用什么就加载什么。类加载器是有其自身的体系的。每一个java程序至少拥有三个类加载器:
1.引导类加载器:负责加载系统类,通常从rt.jar中加载,jre/lib目录;
2.扩展类加载器:用于从jre/lib/ext目录加载扩展类。
3.系统类加载器:用于加载应用类,可以找到classpath里面指定的jar中的类。
类加载器的层次结构
类加载器有一个等级结构,可以认为默认的是上层优先的原则。除了引导类加载器外其他的类加载器都有一个父类加载器。根据规定,每个类加
载器都会先为其父类加载器提供工作机会,如果父类不能完成此项任务,他才会自己去做。
研究类的加载有助于了解JVM执行过程,让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。
一、简单过程
Java程序运行的场所是内存,当在命令行下执行:
java HelloWorld 命令的时候,JVM会将HelloWorld.class加载到内存中,并形成一个Class的对象HelloWorld.class。其中的过程就是类加载过程:
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。
4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。
5、最后由AppClass Loader加载HelloWorld类。
以上就是类加载的最一般的过程。
类加载器的特点:
1、运行一个程序时,总是由AppClass Loader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3、Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
类加载有三种方式:
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
同一个ClassLoader加载的类文件,只有一个Class实例。但是,如果同一个类文件被不同的ClassLoader载入,则会有两份不同的ClassLoader实例
6.关于寻找class文件原理??
建议大家在入门的时候在命令行窗口编译和运行,不要借助JCreator或者Eclipse等IDE去帮助做那些事情。尝试自己这样做:
javac -classpath yourpath *.java
java -classpath yourpath *.class
也许很多人都能看懂,设置classpath的目的就是告诉编译器去哪里寻找你的class文件. JVM去查询类的原理,编译器加载类要依靠classloader,
而classloader有3个级别,从高到低分别是BootClassLoader(名字可能不准确) , ExtClassLoader, AppClassLoader.
这3个加载器分别对应着编译器去寻找类文件的优先级别和不同的路径:
BootClassLoader对应jre/classes路径,是编译器最优先寻找class的地方
ExtClassLoader对应jre/lib/ext路径,是编译器次优先寻找class的地方
AppClassLoader对应当前路径,所以也是编译器默认找class的地方
其实大家可以自己写个程序简单的测试,对任何class,例如A,
调用new A().getClass().getClassLoader().toString() 打印出来就可以看到,把class文件放在不同的路径下再次执行,就会看到区别。
特别注意的是如果打印出来是null就表示到了最高级 BootClassLoader, 因为它是C++编写的,不存在Java对应的类加载器的名字。
寻找的顺序是一种向上迂回的思想,即如果本级别找不到,就只能去本级别之上的找,不会向下寻找。
这样希望大家不至于迷惑为什么总报错找不到类文件,不管是自己写的还是导入的第三方的jar文件(J2ee中经常需要导入的)。
7.Java反射机制
Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。
Java可以加载一个运行时才得知名称的class,获得其完整结构
Java反射机制提供如下功能:
————在运行时判断任意一个对象所属的类
————在运行时构造任意一个类的对象
————在运行时判段任意一个类所具有的成员变量和方法
————在运行时调用任一个对象的方法
————在运行时创建新类对象
————在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。
8.JAVA的内存管理
A:
————Java的内存管理就是对象的分配和释放问题。
————在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数
来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。
Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了
能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了
更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。
在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。
JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。
————在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点:
首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;
其次,这些对象是无用的,即程序以后不会再使用这些对象。
如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存
典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,
这些引用会自动被清理。
内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。
eg:
某个(某些)对象已经不在被使用应该被gc所回收,但有一个对象持有这个对象的引用而阻止这个对象被回收。比如我 们通常会这样创建一个
View TextView tv = new TextView(this);这里的this通常都是Activity。所以这个TextView就持有着这个Activity的引用
通常情况下,当用户转动手机的时候,android会重新调用OnCreate()方法生成一个新的Activity,原来的 Activity应该被GC所回收。但如
果有个对象比如一个View的作用域超过了这个Activity(比如有一个static对象或者我们把这个 View的引用放到了Application当中),这时候原
来的Activity将不能被GC所回收,Activity本身又持有很多对象的引用,所以 整个Activity的内存被泄漏了。
经常导致内存泄漏的一些原因:
1,一个View,的作用域超出了所在的Activity的作用域,比如一个static的View或者 把一个View cache到了application当中 etc
2,某些与View关联的Drawable的作用域超出了Activity的作用域。
3,Runnable对象:比如在一个Activity中启用了一个新线程去执行一个任务,在这期间这个Activity被系统回收了, 但Runnalbe的任务还
没有执行完毕并持有Activity的引用而泄漏,但这种泄漏一般来泄漏一段时间,只有Runnalbe的线程执行完闭,这个 Activity又可以被正常回收了。
4,内存类的对象作用域超出Activity的范围:比如定义了一个内存类来存储数据,又把这个内存类的对象传给了其它Activity 或者Service等。
因为内部类的对象会持有当前类的引用,所以也就持有了Context的引用。解决方法是如果不需要当前的引用把内部类写成 static或者,把内部类
抽取出来变成一个单独的类,或者把避免内部对象作用域超出Activity的作用域。
out Of Memery Error 在android中每一个程序所分到的内存大小是有限的,如果超过了这个数就会报Out Of Memory Error。android给程序分
配的内存大小与手机硬件有关,以下是一些手机的数据:
G1:16M Droid:24 Nexus One:32M Xoom:48Ms
所以尽量把程序中的一些大的数据cache到本地文件。以免内存使用量超标。
9..Java和C++的区别
A:
————速度:解释过的Java要比C的执行速度慢上约20倍
Java是运行在JVM中的,并且是编译成JVM可识别加载的.class,并不是完全编译成计算机直接可执行的程序
————JAVA没有结构、联合,只有“类”(Class)!
————Java的“主类型”(Primitive type),boolean,char,byte,short,int,long,float以及double。所有主类型的大小都是固有的,
且与具体的机器无关(考虑到移植的问题)。
————Java的主类型都只能在堆栈上创建,Java的对象都从堆中分配
————Java没有象C和C++的指针,用new创建一个对象的时候,会获得一个引用
————JAVA有垃圾回收机制,c++用析构函数回收垃圾。
————JAVA的应用在高层,C++在中间件和底层
————Java不提供多重继承机制,但Java提供了一个interface关键字
10.泛型
A:
————泛型是在java5才加入的,简单的讲就是对集合中的类型进行约束,也增强了对象的安全。
————泛型就是给数据集合强制限定了存储数据的类型。是在编译期间约束类型的,保证对象安全。
比如 ArrayList<String>a=new ArrayList<String>();
上面这个ArrayList里只能存储String类型的数据,如果你往里放Integer的话就会报错,而且取出数据的时候也是String,不需要强制转换类型。
————在java5之前所有放进数据集合中的数据都会被转成object对象存储,而且不管什么时候都可以存的进去,你可以先放一个String然后再放一个
Integer这样也能编译通过,当取出数据的时候,取出都是object类型的数据,必须进行强制类型转换转换成原来的数据类型才能使用。
11.HashMap遍历的两种方式,推荐使用entrySet()
A:
————HashMap的遍历有两种常用的方法,那就是使用 entryset 及 keyset 来进行遍历,但两者的遍历速度是有差别的;
keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value; ——效率低,尽量少使用
entryset只是遍历了第一次,他把key和value都放到了entry中; ——效率高,推荐使用此种方式
(1)第一种:
Map map=new HashMap();
Iterator iter=map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry=(Map.Entry)iter.next();
Object key=entry.getKey();
Object val=entry.getValue();
}
(2)第二种
Map map=new HashMap();
Iterator iter=map.keyset.iterator();
while(iter.hasNext()){
Object key=iter.next();
Object val=map.get(key);
}
为什么差别这么大呢?这两种遍历方式代码几乎完全雷同,只是在获取Value对象的时候不一样。
————entrySet遍历方式获取Value对象是直接从Entry对象中直接获得,时间复杂度T(n)=o(1);
————keySet遍历获取Value对象则要从Map中重新获取
keySet遍历Map方式比entrySet遍历Map方式多了一次循环,多遍历了一次,当Map的size越大时,遍历的效率差别就越大
12.HashMap与HashTable的区别
(1)hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
(2)hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
(3)hashMap允许空键值,而hashTable不允许。
13.List、Map、Set三个接口,存取元素时,各有什么特点?
List 以特定次序来持有元素,可有重复元素。Set 无法拥有重复元素,内部排序。Map 保存key-value值,value可多值
14.HTTPS和HTTP的区别
A:
HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议
它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,
单来说它是HTTP的安全版。 它是由Netscape开发并内置于其浏览器中,用于对数据进行压缩和解压操作,并返回网络上传送回的结果。
HTTPS实际上应用了Netscape的安 全全套接字层(SSL)作为HTTP应用层的子层。(HTTPS使用端口443,而不是象HTTP那样使用端口80
来和TCP/IP进行通信。)SSL使 用40 位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。HTTPS和SSL支持使用X.509数字
认证,如果需要的话用户可以确认发送者是谁。
HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全
HTTP代理:
HTTP代理是基于TCP的socket连接,就是A无法直接连接C,但B即可以连接A,也可以连接C
那么需要B开启他的代理服务,设置服务器并监听端口。
A连接B的80或者8080代理服务器端口,建立socketAB,发送(连接C的WEB请求)到B,B收到该请求后进行解析,然后B去连接C,建立socketBC,
并通过socketBC把(连接C的WEB请求)发送给C。
反之依然。
也就是说TCP层知道是A->B->C,但是HTTP封装的包只知道是A->C
hash:
Hash,一般翻译做“散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长
度的输出,该输出就是散列值。
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.
也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系
Hash算法是根据目标数据的内容生成一个完全依赖于目标数据内容的字节序列,同样内容的数据,经过Hash算法,生成同样的字节序列。
Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。
反射
————反射就是让你可以通过名称来得到对象 ( 类,属性,方法 ) 的技术;
例如我们可以通过类名来生成一个类的实例;知道了方法名,就可以调用这个方法;知道了属性名就可以访问这个属性的值。
java对象锁
A:
————在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。
————synchronized对象的锁:
所有对象都自动含有单一的锁。
当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。
三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象
synchronized (this)),所以对它们的方法依然是互斥的。
三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,这些同步块处在不同的方法中,并且是同步到三个不同的对象(synchronized
(this),synchronized(syncObject1),synchronized (syncObject2)),所以对它们的方法中的临界资源访问是独立的。
————Lock对象锁
为什么一般要用http来进行网络编程,而用 socket 的比较少? sockest有什么缺点?
A:
————TCP或者UDP协议其实都是基于Socket来实现的;
————http在tcp之上,要先用socket建立tcp才能实现http。http只不过帮你把底层实现屏蔽了,面向对象的特点。所以Socket效率高些.
HTTP协议:超文本传输协议,是一种通信协议(如FTP、Telnet、SMTP、HTTP、RIP),如对应于应用层 ,HTTP协议是基于TCP连接的
————socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层;
————两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现,是以不同的应用层协议来定义的`如HTTP`FTP`...
socket是对端口通信开发的工具,它要更底层一些
TCP和UDP的区别:
————TCP是面向链接的,虽然说网络的不安全不稳定特性决定了多少次握手都不能保证连接的可靠性,但TCP的三次握手在最低限度上保证了连接的可靠性;
————UDP不是面向连接的,UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,
所以说UDP是无连接的、不可靠的一种数据传输协议。
————UDP的开销更小数据传输速率更高,因为不必进行收发数据的确认,所以UDP的实时性更好。
————知道了TCP和UDP的区别,就不难理解为何采用TCP传输协议的MSN比采用UDP的QQ传输文件慢了,但并不能说QQ的通信是不安全的,因为程序员可以手动
对UDP的数据收发进行验证,比如发送方对每个数据包进行编号然后由接收方进行验证啊什么的,即使是这样,UDP因为在底层协议的封装上没有采用类
似TCP的“三次握手”而实现了TCP所无法达到的传输效率。
HTTP通讯的get和post
A:
GET和POST
————HTTP协议中的两种不同的请求方式——GET和POST。
————GET方式在进行数据请求时,会把数据附加到URL后面传递给服务器,比如常见的:http://XXX.XXX.XXX/XX.aspx?id=1,
————POST方式则是将请求的数据放到HTTP请求头中,作为请求头的一部分传入服务器。所以,在进行HTTP编程前,首先要明
确究竟使用的哪种方式进行数据请求
----------------------------------------------Android系列问题----------------------------------------------------
1.Android的activity启动的几种模式简介。
A:
————launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity
实例,是否和其他Activity实例公用一个task里。
————Activity一共有以下四种launchMode:standard / singleTop / singleTask/ singleInstance
可以在AndroidManifest.xml配置<activity>的android:launchMode属性为以上四种之一
standard模式是默认的启动模式,不用为<activity>配置android:launchMode属性即可,当然也可以指定值为standard。
每次跳转系统都会在task中生成一个新的FirstActivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的FirstActivity实例。
【standard启动模式:不管有没有已存在的实例,都生成新的实例】
【singleTop启动模式:如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例】
【singleTask模式:如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前】
【singleInstance模式:启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入】
2.如何区分activity/task/process
A:
————task是一个具有栈结构的对象,可以理解为一个栈。一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。
————process,一个应用里面可以有多个PId,一个pid对应一个进程,每次打开是系统都会赋予不同的pid,但是uid貌似是不变的;
【android为每个应用几乎都分配了不同的UID,在android中PID,和UID都是用来识别应用程序的身份的;
但UID是为了不同的程序来使用共享的数据: ①通过UID共享数据,在manifest配置相同的android:sharedUserId;
②不同的程序能够相互访问,还需要拥有相同的签名;】
ActivityManager类可以获取运行信息,如下:
getRecentTasks() 最近开的task,HOME键长按会看到这个
getRunningAppProcesses() 运行中的作为app容器的process
getRunningServices() 运行中的后台服务
getRunningTasks() 运行中的任务
3.Android 多个APK数据共享
A:
Android给每个APK进程分配一个单独的用户空间,其manifest中的userid就是对应一个Linux用户,所以不同APK(用户)间互相访问数据默认是禁止的.
但是它也提供了2种APK间共享数据的形式:
————Share Preference. / Content Provider
APK可以指定接口和数据给任何其他APK读取. 需要自己实现接口和Share的数据.
————Shared User id
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也
可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样.
4.android内存管理的原理
Android采取了一种有别于Linux的进程管理策略,有别于Linux的在进程活动停止后就结束该进程,Android把这些进程都保留在内存中,直到系
统需要更多内存为止。这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度。
那Android什么时候结束进程?结束哪个进程呢?
Android将进程分为六大类,对进程的重要性进行评估:
————1.前台进程(foreground):目前正在屏幕上显示的进程和一些系统进程。
————2.可见进程(visible):可见进程是一些不再前台,但用户依然可见的进程,
————3.次要服务(secondary server):目前正在运行的一些服务
————4.后台进程(hidden):就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。一旦我们按home返回主界面(注意
是按home,不是按back),程序就驻留在后台,成为后台进程
————5.内容供应节点(content provider):没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。
————6.空进程(empty):有些程序,在程序退出后,依然会在进程中驻留一个空进程,进程里没有任何数据在运行,作用往往是提高该程序下
次的启动速度或者记录程序的一些历史信息。
5. 如何优化UI
解析布局文件花费的时间取决于布局的复杂性:资源数据越大解析越慢
1. 使用相对布局代替线性布局或其他布局
2. 合并布局,如果自己布局的顶层元素是一个FrameLayout,可以使用<merge />标签来合并布局
3. 重用布局,使用 <include />
有两个目的:1. 多次使用相同的布局, 2. 布局有一个通用的组成部分,或有部分依赖于设备配置
4. ViewStub
ViewStub是轻量级且不可见的视图,当需要时,在自己的布局中可以用它来推迟展开布局。
布局优化工具,可以使用HierarchyViewer和layoutopt,这两个工具都位于SDK的tools目录下
6.说说应用saveInstance 与 restoreInstance区别,如何使用?
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,
它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()才会被调用。
但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需
要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建
后恢复缓存的数据。可以重写Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。
7.android view,surfaceview,glsurfaceview的区别:
SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使
用SurfaceView;
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面;而View必须在UI的主线程中更新画面。
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,
触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一
下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类。
1)被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。因为这种情况下,
这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
2)主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需
要surfaceView来控制。
8.shareUserId简介。
A:
Android给每个APK进程分配一个单独的空间,manifest中的userid就是对应一个分配的Linux用户ID,并且为它创建一个沙箱,以防止影响
其他应用程序(或者其他应用程序影响它)。用户ID 在应用程序安装到设备中时被分配,并且在这个设备中保持它的永久性。
通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,在保障了程序运行的稳定。然后
在有些时候,我们自己开发了多个APK并且需要他们之间互相共享资源,那么就需要通过设置shareUserId来实现这一目的。
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也可以配置成
运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。
9..Activity跳转流程
A:
————Activity从Main跳转到Subscreen过程函数调用顺序:
Main.onCreate()→Main.onStart()→Main.onResume()→Main.onPause()→Subscreen.onCreate()→Subscreen.onStart()→Subscreen.onResume()
→Main.onStop()
————Android在运行时,正常情况下,Activity的切换其实是将运行过的Activity压入栈中,每创建一个Activity就向栈中压入该Activity;
当点击返回键的时候会销毁当前Activity,即将当前Activity从栈顶删除,接着显式栈中的第二个Activity(也即现在的栈顶Activity)。
【注意:点击返回键是会destroy掉当前Activity的,而不是再将该Activity压入栈里】
【后台键home——activity onstop; 返回键back——activity onDestroy】
————Activity跳转,onPause,onStop差别,当之前Activity切换到后台,但是仍然可见的时候,执行onPause;
当之前Activity切换到后台,而且完全不可见时候,执行onStop;
10.在android中,请简述jni的调用过程?
A:
1)安装和下载Cygwin,下载 Android NDK
2)在ndk项目中JNI接口的设计
3)使用C/C++实现本地方法
4)JNI生成动态链接库.so文件
5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可
11.如何退出Activity?如何安全退出已调用多个Activity的Application?
A:
————单一Activity的应用来说,退出很简单,直接finish()即可。也可以用killProcess()和System.exit()这样的方法。
————多Activity的应用来说,记录打开的Activity, 在需要退出时,关闭每一个Activity即可;
或者发送特定广播,在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可;
12.如何启用Service,如何停用Service
A:
————service一般没有用户操作界面,它运行于系统中不容易被用户发觉,实现步骤如下
第一步:继承Service类 public class BluetoothConnController extends Service{}
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置: <service android:name=".DemoService" />
第三步:Context.startService()和Context.bindService
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,
但是它们的使用场合有所不同。
1.使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用
服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
2.采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
3.采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个
时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()
方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)
。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
————Service的生命周期
1.Service常用生命周期回调方法如下:
onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onDestroy()该方法在服务被终止时调用。
2. Context.startService()启动Service有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽
管不会多次创建服务,但onStart() 方法会被多次调用。
3. Context.bindService()启动Service有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次
调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
备注:
1. 采用startService()启动服务
Intent intent =new Intent(DemoActivity.this, DemoService.class);
startService(intent);
2.Context.bindService()启动
Intent intent =new Intent(DemoActivity.this, DemoService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//unbindService(conn);//解除绑定
13.请解释下在单线程模型中Message、Handler、MessageQueue、Looper之间的关系 ???
A:
Handler简介:
一个Handler允许你发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联。每一个线程实例和一个单独的线程以及该线程
的MessageQueue相关联。当你创建一个新的Handler时,它就和创建它的线程绑定在一起了。这里,线程我们也可以理解为线程的MessageQueue。从这一
点上来看,Handler把Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler负责执行他们。
Handler有两个主要的用途:
(1)确定在将来的某个时间点执行一个或者一些Message和Runnable对象。
(2)在其他线程(不是Handler绑定线程)中排入一些要执行的动作。
postDelayed\sendMessage等操作把Message对象加入MessageQueue,这些Message对象包含一些信息,Handler的hanlerMessage(Message)会处理这些
Message.当然,handlerMessage(Message)必须由Handler的子类来重写。这是由编程人员实现。
Message简介:
Message类就是定义了一个信息,这个信息中包含一个描述符和任意的数据对象,这个信息被用来传递给Handler.Message对象提供额外的两个int域和
一个Object域,这可以让你在大多数情况下不用作分配的动作。
尽管Message的构造函数是public的,但是获取Message实例的最好方法是调用Message.obtain(),或者Handler.obtainMessage()方法,这些方法会从
回收对象池中获取一个。
MessageQueue简介:
包含message列表的底层类。Looper负责分发这些message。Messages并不是直接加到一个MessageQueue中,而是通过MessageQueue.IdleHandler关联到
Looper。你可以通过Looper.myQueue()从当前线程中获取MessageQueue。
Looper简介:
Looper类被用来执行一个线程中的message循环。默认情况,没有一个消息循环关联到线程。在线程中调用prepare()创建一个Looper,然后用loop()
来处理messages,直到循环终止。
???待完善修改
14.AIDL的全称是什么?如何工作?能处理哪些类型的数据?
A:
AIDL的英文全称是Android Interface Define Language,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
icp:interprocess communication :内部进程通信
【java中RMI介绍
RMI:(远程接口调用)
1. RMI的原理:
RMI系统结构,在客户端和服务器端都有几层结构。
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传
输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程
引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的远程
对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传
输层和远程调用层返回。最后,占位程序获得返回值。
2. RMI(远程方法调用)的组成
一个正常工作的RMI系统由下面几个部分组成:
远程服务的接口定义
远程服务接口的具体实现
桩(Stub)和框架(Skeleton)文件
一个运行远程服务的服务器
一个RMI命名服务,它允许客户端去发现这个远程服务
类文件的提供者(一个HTTP或者FTP服务器)
一个需要这个远程服务的客户端程序
】
当B进程要去调用A进程中的service时,并实现通信,我们通常都是通过AIDL来操作的。建立AIDL服务步骤:
————A工程:
①Java包目录中创建一个RemoteService.aidl文件,在里面我们自定义一个接口,含有方法get。
【注:AIDL规定,文件名必须和interface XXX名字相同,否则会报错】
如果aidl文件的内容是正确的,ADT插件会在gen目录下自动生成一个Java接口文件(*.java),RemoteService.java文件,该类中含有一个名为
RemoteService.stub的内部类(远程服务的本地代理类,Stub对象是在被调用端进程,也就是服务端进程),该内部类中含有aidl文件接口的get方法。
②然后定义自己的MyService类;
1.在MyService类中自定义一个内部类去继承RemoteService.stub这个内部类,实现get方法。
/* 该类是MyService的内部类
* 该类继承了RemoteService.stub类而不是extends Binder类。
* RemoteService.stub是Binder的子类。
* 进程内的Service定义MyBinder内部类是继承Binder类。
*/
public class MyBinder extends RemoteService.stub {
@Override
public String get() throws RemoteException {
return MyService.this.get();
}
}
private String get() {
String str="MyService 自定义 get()...";
return str;
}
2.MyService类需要实现onBind方法,在onBind方法中返回这个内部类的对象,系统会自动将这个对象封装成IBinder对象(即步骤3中的IBinder serviceXXX),
传递给他的调用者。
private MyBinder mBinder;
public IBinder onBind(Intent intent) {
Log.i(tag, "...service onBind()...");
return mBinder;
}
3.需要在AndroidManifest.xml文件中配置MyService类,代码如下:
<!-- 注册服务 -->
<service android:name=".MyService">
<intent-filter>
<!-- 指定调用AIDL服务的ACTION -->
<action android:name="net.blogjava.mobile.aidlservice.RemoteService" />
</intent-filter>
</service>
指定调用AIDL服务的ACTION,如果其他进程知道这个ACTION,MyService这个类能够被别的进程访问,有了这个ACTION,B工程才能找到A工程实现通信。
————B工程:
①将A工程中生成的RemoteService.java文件拷贝到B工程中
②调用bindService方法绑定aidl服务,
bindService(newIntent("net.blogjava.mobile.aidlservice.RemoteService"),serviceConnection, Context.BIND_AUTO_CREATE);
第一个参数 Intent , 用AIDL服务的ACTION创建INTENT
注意<action>标签中的android.name 的属性值就是客户端要引用该服务的ID,也就是Intent.setAction("xxxxxxx");(或者Intent构造方法的参数)。
注意,这里在Client进程中不能通过new Intent(context,IMyService.class) 来启动
第二个参数ServiceConnection,即执行跨进程绑定服务。
在客户端的onServiceConnected方法中用IMyService.Stub.asInterface()方法转换服务器端传递过来的IBinder对象;
获得从服务中返回的对象后,通过调用该对象中的方法或属性值达到与被绑定的服务交换数据和控制该服务的目的
RemoteService mService; //接口的一个引用
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log("connect service");
mService = RemoteService.Stub.asInterface(service); //获得另一个进程中的Service传递过来的IBinder对象-mService
try {
mService.registerTestCall(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
Log("disconnect service");
mService = null;
}
};
15.什么是ANR 如何避免它?
A:
————ANR:Application Not Responding,应用无响应
在Android里,ActivityManager(活动管理器)和WindowManager(窗口管理器)这两个系统服务监控着应用程序的响应能力。Android会在检测到
以下情形中之一时,弹出ANR对话框:
1.未在5秒内对用户输入事件响应; ———— 对输入事件(如按键、触摸屏事件)的响应超过5秒
2.BroadcastReceiver未在10秒内执行完 ————
————Android应用默认运行在单线程里,叫UI线程或主线程。应用所有工作都在UI线程里,如果花费很长时间才能完成,会触发ANR,因为此时应用无法
操控输入事件或广播。
因此,UI 线程里的任何方法都应该尽可能地做轻量的工作,特别是Activity在生命周期方法,像onCreate(),onResume().潜在的耗时操作,像网络,
数据库,或昂贵的计算(像改变图片大小)应该在工作线程里完成(或者在数据库操作案例里,通过一个异步请求)。
但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束,不需要调用Therad.wait()或者Thread.sleep()方法,取而代之的是,主线程为
子线程提供一个句柄(Handler),让子线程在即将结束的时候调用它,能够保证程序对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处
理而产生的ANR。
【特别强调BroadcastReceiver的执行时间,执行密集任务应该用startService启动IntentService,IntentService在oncreat中会创建一个异步线程】
16.如何将SQLite数据库(dictionary.db文件)与apk文件一起发布?
A:
————assets目录与res/raw 目录的区别
1.assets目录与res/raw两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
res/raw不参加编译的资源,(已经建好的数据库,图片等),可以用于在程序首次运行时,直接复制到sd卡上
2.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;
assets文件夹下的文件不会被映射到R.java中,读取assets目录下的文件必须指定文件的路径,访问的时候需要AssetManager类。
3.读取文件资源:
读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作 InputStream is = getResources().openRawResource(R.id.filename);
读取assets下的文件资源,通过以下方式获取输入流来进行写操作 AssetManager am = getResources().getAssets();
InputStream is = am.open("filename");
————可以将dictionary.db文件复制到Android工程中的res/raw目录中。所有在res/raw目录中的文件不会被压缩,这样可以直接提取该目录中
的文件。
17.如何打开res/raw目录中的数据库文件?
A:
无论是raw文件夹还是assets文件夹,都是在生成apk的时候不编译而直接携带在apk的压缩包中的,这可以打开apk检验;
提取的方法都是从inputstream转,转成什么形式的,要看对inputstream的操作;基本方法是使用getResources().openRawResource方法
获得res aw目录中资源的 InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。
在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。
raw & assets资源比较重要的一点,就是文件的大小限制,单个文件的大小不可以大于1M。
解决的方法是: 将文件split为小于1M的文件,在读取的时候outputstream不要close,而是合并写这些文件,最后就得到原始文件。
18.android中的动画有哪几类,它们的特点和区别是什么?
A:
————两种,一种是Tween动画、还有一种是Frame动画。
Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;
Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。
19.handler机制的原理? ???
A:
andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。
1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
20.DDMS和TraceView的区别? ???TraceView
A:
DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,
TraceView是程序性能分析器 。
21.谈谈Android的IPC(进程间通信)机制
A:
————IPC是内部进程通信的简称, 是共享"命名管道"的资源。
————Android中的IPC机制是为了让Activity和Service之间可以随时的进行交互,故在Android中该机制,只适用于Activity和Service之间的通信,类似于
远程方法调用,类似于C/S模式的访问。通过定义AIDL接口文件来定义IPC接口。Server端实现IPC接口,Client端调用IPC接口本地代理。
22.NDK是什么
A:
NDK是一些列工具的集合,NDK提供了一系列的工具,帮助开发者迅速的开发C/C++的动态库,并能自动将so和java 应用打成apk包。
NDK集成了交叉编译器,并提供了相应的mk文件和隔离cpu、平台等的差异,开发人员只需简单的修改mk文件就可以创建出so。
23.瀑布流是怎么实现的?
A:
————其实瀑布流的大体布局是由一个ScrollView和LinearLayout组成,LinearLayout包裹在ScrollView中,而其中的每一列也是一个LinearLayout布局,
内部为垂直分布,根据要求来显示分为n列,通过要求的列数来判断创建n个LinearLayout布局,而每个列的宽度是整个屏幕宽度的1/n,高度为铺满全屏,
通过将请求来的图片进行每组n个的依次循环往n列中各个填充。
24.侧滑是怎么实现的
A:
————首先还是讲一下实现原理。在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局。两个布局横向排列,
菜单布局在左,内容布局在右。初始化的时候将菜单布局向左偏移,以至于能够完全隐藏,这样内容布局就会完全显示在Activity中。然后通过监听手
指滑动事件,来改变菜单布局的左偏移距离,从而控制菜单布局的显示和隐藏。
25.你对缓存的理解,为什么要进行缓存
A:
————客户端缓存机制是android应用开发中非常重要的一项工作,使用缓存机制不仅仅可以为用户节省3G流量,同时在用户体验方面也是非常好的选择.
缓存机制分为两部分,一部分是文字缓存,另一部分是多媒体文件缓存.
缓存的优点
1.服务器的压力大大减小
2. 客户端的响应速度大大变快(用户体验)
3. 客户端的数据加载出错情况大大较少,大大提高了应有的稳定性(用户体验)
4. 一定程度上可以支持离线浏览(或者说为离线浏览提供了技术支持)
26.Sqlite怎么进行版本更新
A:
通过SQLiteOpenHelper类中的onUpgrade方法来判断比较版本号进行是否版本更新。
27.Sqlite怎么插入数据,插入一万条数据怎么操作
A:
在执行SQL语句前开启事务,在执行完后再关闭事务。
例如:
long starttime = System.currentTimeMillis();
System.out.println(starttime+"");
myDB.beginTransaction();
for (int i = 0; i< 2000;i++){
myDB.execSQL("insert into meetings (id ,mainid) values ( '1','1')");
}
myDB.setTransactionSuccessful();
myDB.endTransaction();
28.Activity的生命周期,点击home键时生命周期是什么样的?弹出一个窗口的时候生命周期是什么?
A:
————Activity生命周期:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestroy()->onReStart()
————Home键: onPause()->onStop()
————窗口: 一种是onPause()(未覆盖整个activity)
一种是onPause()->onStop()(覆盖了整个activity)
29.如何把activity设置成窗口模式
A:
在AndroidManifest.xml中相应的<activity>标签内设置属性android:theme=”@android:style/Theme.Dialog”
30.用过自定义的View吗?如何用
A:
RoundProgressBar
————自定义View其实就是继承View,并实现onDraw(),OnMeasure(),OnLayout()方法。
①如果需要改变View绘制的图像,那么需要重写OnDraw方法。(这也是最常用的重写方式。)
②如果需要改变view的大小,那么需要重写OnMeasure方法。
③如果需要改变View的(在父控件的)位置,那么需要重写OnLayout方法。
31.Arraylist和linkedlist有什么区别,它们都是线程安全的吗?
A:
ArrayList数组实现,轻量级(消耗系统资源少),运行快,线程不安全;
LinkedList 链表实现 常用于堆栈与队列的实现,不是线程安全的;
注: 数组元素连续顺序存放,查询快, 增删慢
链表元素顺序存放,查询慢, 增删快
32.用没用过事务,怎么用
A:
————sqlite 是支持事务处理的。
如果要同步删除很多数据,把它们做成一个统一的事务。通常一次 sqlite3_exec 就是一次事务,如果你要删除1万条数据,sqlite就做了1万次:
开始新事务->删除一条数据->提交事务->开始新事务->… 的过程。这个操作是很慢的。因为时间都花在了开始事务、提交事务上。
可以把这些同类操作做成一个事务,如下题所示。
33.Sqlite怎么插入数据,插入一万条数据怎么操作
A:
————在执行SQL语句前开启事务,在执行完后再关闭事务。
例如:
long starttime = System.currentTimeMillis();
System.out.println(starttime+"");
myDB.beginTransaction();
for (int i = 0; i< 2000;i++){
myDB.execSQL("insert into meetings (id ,mainid) values ( '1','1')");
}
myDB.setTransactionSuccessful();
myDB.endTransaction();
34.Android中的五种存储方式及其应用场景
A:
1)SharedPreferences
存储路径:(data/data/packagename/shares_prefs), 轻量级存储,以键值对的形式存储在xml中,一般用来保存应用中的设置属性
2)文件存储 SD卡存储多媒体文件, 文件缓存
3) Sqlite数据库 存储路径:(data/data/packagename/databases), 一种嵌入式数据库,支持sql语言,存储大量结构性数据
4)ContentProvider 进程(应用程序)间数据共享,数据源可以是sqlite,也可以是xml,相关类: ContentResolver(内容解析器), ContentObserver(数据 观察者)
5) 网络存储 天气数据的xml,json格式等等,通过HttpUrlConnection,HttpClient,或者SOAP协议获取数据
35.listview优化策略?
A:
1)、对convetView进行判空,是当convertView不为空的时候直接重新使用convertView。从而减少了很多不必要的View的创建
2)定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可
3)、当ListView加载数据量较大时可以采用分页加载和图片异步加载
36.ListView分页加载实现思路?
A:
————实现OnScrollListener 接口重写onScrollStateChanged 和onScroll方法,使用onscroll方法实现”滑动“后处理检查是否还有新的记录,如果有,
调用 addFooterView,添加记录到adapter, adapter调用 notifyDataSetChanged 更新数据;如果没有记录了,把自定义的mFooterView去掉。使用
onScrollStateChanged可以检测是否滚到最后一行且停止滚动然后执行加载
37.ListView图片异步加载实现思路?
A:
1.先从内存缓存中获取图片显示(内存缓冲)
2.获取不到的话从SD卡里获取(SD卡缓冲,,从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅)
3.都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示(视情况看是否要显示)
38.Intent的原理,作用,可以传递哪些类型的参数?
A:
————intent是连接Activity, Service, BroadcastReceiver, ContentProvider四大组件的信使,,可以传递八种基本数据类型以及string, Bundle类型,
以及实现了Serializable或者Parcelable的类型。
Intent可以划分成显式意图和隐式意图。
显式意图:调用Intent.setComponent()或Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给
哪个组件。
隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据类型)
找到最合适的组件来处理这个意图。
39.如何实现屏幕分辨率的自适应?
A:
————尽量使用Relativelayout
————通过权重(layout_weight)的方式来分配每个组件的大小比例,使用dip设备独立像素,不依赖像素
————drawable-hdpi,drawable-mdpi,drawable-ldpi放置不同分辨率图片
————通过.9.png实现图片的自适应
————已知应用支持平台设备的分辨率,可以提供多个layout_320*480
40.简述Android中的IPC机制
A:
————IPC(Inter-Process Communication,进程间通信),
————aidl是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口.
编译器可以通过扩展名为aidl的文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的.
————BroadcastReceiver也可以实现进程间通信
————ContentProvider 提供进程间数据共享
41.android哪几种方式访问网络?
A:
————HttpURLConnection
————HttpClient方式(HttpGet和HttpPost类)
HttpClient的通信过程
1.生成请求对象(HttpGet get,HttpPost post)
2.生成客户端对象 HttpClient client
3.执行请求接收相应 HttpResponse response = client.execute(post)
HttpEntity entity = response.getEntity()
4.得到数据流
InputStream inputStream = entity.getContent();
5.最后关闭过期连接
42.移动互联数据交互格式有哪些及其区别?(Json与xml的区别?)
A:
————移动互联数据交互格式有XML和JSON
1.JSON和XML的数据可读性基本相同
2.JSON和XML同样拥有丰富的解析手段
3.JSON相对于XML来讲,数据的体积小
4.JSON与JavaScript的交互更加方便
5.JSON对数据的描述性比XML较差
6.JSON的速度要远远快于XML
43.XML解析有哪几种?各自优缺点,官方推荐使用哪种?
A:
————基本的解析方式有三种: DOM,SAX,Pull
1.dom解析解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用 DOM 接口来操作这个树结构的优点是对文档增删改查比较方便,
缺点占用内存比较大。
2.sax解析基于事件驱动型,优点占用内存少,解析速度快,缺点是只适合做文档的读取,不适合做文档的增删改查。
3.pull解析同样基于事件驱动型,android 官方API提供,可随时终止
44.GC内存泄露在什么情况下回出现?怎么解决?
A:
————查询数据库没有关闭游标
————构造Adapter时,没有使用缓存的 convertView
————Bitmap对象不在使用时调用recycle()释放内存
————不用的对象没有及时释放对象的引用
45.android内存的优化
A:
android内存泄露容易导致内存溢出,又称为OOM。
Android内存优化策略:
1)在循环内尽量不要使用局部变量
2)不用的对象即时释放,即指向NULL
3)数据库的cursor即时关闭。
4)构造adapter时使用缓存contentview
5)调用registerReceiver()后在对应的生命周期方法中调用unregisterReceiver()
6)即时关闭InputStream/OutputStream。
7)android系统给图片分配的内存只有8M, 图片尽量使用软引用, 较大图片可通过BitmapFactory缩放后再使用,并及时recycle
8)尽量避免static成员变量引用资源耗费过多的实例。
46.加载大图片的时候如何防止内存溢出
A:
android系统给图片分配的内存只有8M,当加载大量图片时往往会出现OOM。
Android加载大量图片内存溢出解决方案:
1)尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都
是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView
的 source
2)使用BitmapFactory.Options对图片进行压缩
InputStream is = this.getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 10; //width,hight设为原来的十分一
Bitmap btp =BitmapFactory.decodeStream(is,null,options);
3)运用Java软引用,进行图片缓存,将需要经常加载的图片放进缓存里,避免反复加载及时销毁不再使用的Bitmap对象
if(!bmp.isRecycle() ){
bmp.recycle() //回收图片所占的内存
system.gc() //提醒系统及时回收
}
————内存溢出是因为程序运行所需要的内存大于系统能分配给你的内存。看那些内存可以优化的就优化,比如图片过多就用软引用,弱引用。
虽然java不能自己释放内存。但你用过的对象,及时消除引用。再调用一下gc我觉得有时候还蛮好使的
47.Android缓存机制
A:
客户端缓存机制是android应用开发中非常重要的一项工作,使用缓存机制不仅仅可以为用户节省3G流量,同时在用户体验方面也是非常好的选择,比
如有些新闻客户端支持离线模式,也是通过缓存机制实现的.缓存机制分为两部分,一部分是文字缓存,另一部分是多媒体文件缓存.
文字缓存有两种实现:
1)可以将与服务器交互得到的json数据或者xml数据存入sd卡中,并在数据库添加该数据的记录.添加数据库记录时,提供两个关键字段,一个是请求
的URL,另一个则是本地保存后的文件地址,每次加载数据之前都会根据URL在数据库中检索
2)将JSON数据解析后装入List<Map>对象中,然后遍历List,将数据统统写入相应的数据库表结构中,以后每次向服务器发起请求之前可以先在数据库
中检索,如果有直接返回.
多媒体文件缓存:主要指图片缓存
图片的缓存可以根据当前日期,时间为名字缓存到SD卡中的指定图片缓存目录,同时数据库中做相应记录,记录办法可以采用两个关键字段控制,一个字
段是该图片的URL地址,另一个字段是该图片的本机地址.取图片时根据URL在数据中检索,如果没有则连接服务器下载,下载之后再服务器中作出相应记录
缓存文件删除策略:
1. 每一个模块在每次客户端自动或者用户手动更新的时候删除相应模块的缓存文件,并重新下载新的缓存文件.
2. 在设置界面中提供删除缓存的功能,点击后删除本机所有缓存.
48.如何实现消息推送,有哪些方式,各自优缺点,最常使用哪种?
A:
实现消息推送的方式有五种,分别是轮询,SMS,C2DM,MQTT,XMPP最常使用的是XMPP, 我们做项目时采用的是XMPP协议
1.XMPP协议,它是一种基于XML的传递协议,具有很强的灵活性和可扩展性。它的特点是将复杂性从客户端转移到了服务器端。GTalk、QQ、IM等都
用这个协议。
2.轮询:客户端定时去服务端取或者保持一个长Socket,从本质讲这个不叫推送,而是去服务端拽数据。但是实现简单,主要缺点:耗电,浪费用户
流量等
3.Google的C2DM,具体不细说,缺点,服务器在国外,不是很稳定。
4.通过短信方式, 但是很难找到免费短信平台
5. MQTT协议, IBM提供的一种推送服务,不太灵活
49.MVC在Android中的应用
A:
Android中界面部分也采用了当前比较流行的MVC框架,在Android中:
1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。也可以使用JavaScript+HTML等的方式作为View层,通过
WebView组件加载,同时可以实现Java和JavaScript之间的通信。
2) 控制层(Controller):这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是
Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,Android的控制层的重任通常落在了众多的Acitvity的肩上,程序就很容易被回收掉。
3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。
在Android SDK中的数据绑定,也都是采用了与MVC框架类似的方法来显示数据。在控制层上将数据按照视图模型的要求(也就是Android SDK中的Adapter)
封装就可以直接在视图模型上显示了,从而实现了数据绑定。比如显示Cursor中所有数据的ListActivity,其视图层就是一个ListView,将数据封装为
ListAdapter,并传递给ListView,数据就在ListView中显示。
50.Android自定义组件实现思路
A:
Android自定义组件有三种实现思路:
————继承某个现有组件,在其基础上添加额外功能,如继承Gallery实现CoverFlow效果
————继承某个Layout,实现复合组件自定义,如TextView和EditText组合实现登录注册组件
————继承View,实现onDraw()方法,实现自己绘制组件,如翻页效果组件
51.版本更新的实现思路
A:
————在服务器相应URL上有版本文件, 客户端同时存储该应用当前版本号 (SharedPreferences/Sqlite), 每次打开应用,去检测服务器版本号与本地版本号是
否一致,如果不一 致,则自定义对话框提示是否下载更新
52.播放视频有哪些实现方式?
A:
1.使用系统自带的播放器来播放,指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
//调用系统自带的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
2. 使用VideoView组件来播放, 可以结合MediaController来实现播控, 只是不能随意更改视频的大小及位置。
3. 使用MediaPlayer和SurfaceView来实现,这种方式很灵活,可以自定义视频播放的大小和位置。
53.谈谈UI中, Padding和Margin有什么区别,gravity与layout_gravity的区别
A:
Padding 用来指定组件内的内容距离组件边界的距离;
Margin用来指定控件与控件之间的距离
Gravity用来指定组件内的内容相对于组件本身的位置
Layout_gravity用来指定组件相对于其父组件的位置
54.哪个组件可以实现手风琴效果,用来实现设置界面的类,实现抽屉效果, 悬浮窗口?
A:
————实现手风琴效果(ExpandableListView)
————设置界面的类(preferenceActivity)保存到sharedpreference中
————抽屉效果(slidingDrawer)组件
————悬浮窗口: PopWindow,可以实现类似Dialog和菜单的效果
55.常用设计模式及应用场景,用两种方式实现单例模式,要求线程安全?
A:
————常用设计模式:
单例模式:
适配器模式: Adapter 为ListView GridView等添加数据
工厂模式:
代理模式: AIDL
观察者模式: 服务订阅
门面模式:对外统一接口
策略模式:扫描策略
模板模式:提取共用算法, 模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
56.android客户端如何实现自动登录
A:
通过SharedPreferences存储用户名,密码,当存储不为空时实现自动登录功能
57.在Android中,进行http编程有几种方式?
A:
在Android中,可以有两种方式可以用来进行Http编程:1、HttpURLConnection;2、HttpClient
————HttpURLConnection是继承自URLConnection的一个抽象类,在HTTP编程时,来自HttpURLConnection的类是所有操作的基础;
HttpURLConnection对网络资源的请求在默认情况下是使用GET方式的,所以当使用GET方式时,不需要我们做太多的工作:
Http中的get请求,在Url中带有请求的参数,请求的URL格式通常为:"http://XXX.XXXX.com/xx.aspx?param=value"
当我们需要使用POST方式时,就需要使用setRequestMethod()来设置请求方式了。Http中的post请求,不在Url中附加任何参数,
这些参数都会通过cookie或者session等其他方式以键值对的形式key=value传送到服务器上,完成一次请求。请求的URL格式通常为:
"http://XXX.XXXX.com/xx.aspx"
————HttpClient:在Android SDK中提供了Apache HttpClient(org.apache.http.*)模块。但是这个类并不是来自Android的,而是来
自org.apache.http。与HttpURLConnection相同,HttpClient也存在GET和POST两种方式。
HttpGet 在HttpClient中,可以非常轻松使用HttpGet对象来通过GET方式进行数据请求操作,当获得HttpGet对象后我们可以使用
HttpClient的execute方法来向我们的服务器发送请求。在发送的GET请求被服务器相应后,会返回一个HttpResponse响应对象,利用
这个响应的对象我们能够获得响应回来的状态码,如:200、400、401等等
HttpPost 使用POST方式时,我们可以使用HttpPost类来进行操作。当获取了HttpPost对象后,我们就需要向这个请求体传入键值对,
这个键值对我们可以使用NameValuePair对象来进行构造,然后再使用HttpRequest对象最终构造我们的请求体,最后使用HttpClient的
execute方法来发送我们的请求,并在得到响应后返回一个HttpResponse对象。其他操作和我们在HttpGet对象中的操作一样
————HttpGet与HttpPost的异同
共同点,HttpGet和HttpPost创建方式相同:
A.创建HttpGet(或HttpPost)对象,将要请求的URL通过构造方法传入HttpGet(或HttpPost)对象中;
B.使用DefaultHttpClient类的execute方法发送HTTP GET或HTTP POST 请求,并返回HttpResponse对象;
C.通过HttpResponse接口的getEntity方法返回响应信息。
不同点,HttpPost在使用是需要传递参数 ,使用List<NameValuePair>添加参数。
————HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没
有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收响应,以及管理HTTP连接。
57.Android OOM 产生的几种原因?
A:
1. 程序中使用了太多自己创建的Bitmap.
大部分情况是因为重复创建bitmap, 而不使用的bitmap没有被及时释放, 导致了 oom. 所以在不使用的时候要将bitmap对象回收bitmap.recycle(),
并将bitmap对象置为null.
还有就是当你一次性使用过多bitmap的时候也会导致oom. 比如使用系统的Gallery组件, 然后在每一项使用自己的图片, 这个时候我们
通常自定义Gallery的adapter. 当Gallery需要显示很多图片的时候, 而我们没有做图片缓存机制的话必然会导致oom发生. 所以这个时候需
要做图片缓存机制. 例如只保存当前显示项前后3项的图片, 滑动Gallery的时候再根据当前项动态回收前后不需要的图片和加载需要的图片.
2. 程序中一些对象创建的时候需要context的时候.
这种情况, 通常我们会使用create(this);这个时候引用的时候activity的context, 如果该对象没有被及时回收, activity的引用将被它保留,
从而导致activity不能被及时销毁, 当重复创建activity后, 就会导致activity有多个实例,从而导致内存泄露.所以在用到context的时候尽量使用
application的context: getApplicationContext().
3. 查询数据库没有关闭游标
程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor 后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被
发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。所以在使用完游标以后要cursor.close();
4.构造Adapter时,没有使用缓存的convertView
以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法:
publicView getView(int position, View convertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会
从BaseAdapter中根据当前的屏幕布局实例化一定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上
面的list item的view对象会被回收,然后被用来构造新出现的最下面的listitem。这个构造过程就是由getView()方法完成的,getView()的第二个形
参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使
用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。
Android进阶之图片缩略图(解决大图片溢出问题)
大图片的溢出问题,通过缩略图的方式避免。主要是通过BitmapFactory.Options 来实现。
第一步:BitmapFactory.Option设置 inJustDecodeBounds为true
第二步:BitmapFactory.decodeFile(path,option)方法解码图片路径为一个位图。如果指定的文件名是空的,或者不能解码到一个位图,函数将返回null[空值]。
获取到outHeight(图片原始高度)和 outWidth(图片的原始宽度)
第三步:计算缩放比例,也可以不计算,直接给它设定一个值。options.inSampleSize = "你的缩放倍数";如果是2就是高度和宽度都是原始的一半。
第四步:设置options.inJustDecodeBounds = false;重新读出图片 bitmap = BitmapFactory.decodeFile(path, options);
1.缩略图
2.软引用
3.及时回收
大部分的电都消耗在了网络连接、GPS、传感器上了,简单的说也就是主要在以下情况下耗电比较多:
1、 大数据量的传输。
2、 不停的在网络间切换。
3、 解析大量的文本数据。
改善程序
1、在需要网络连接的程序中,首先检查网络连接是否正常,如果没有网络连接,那么就不需要执行相应的程序。
2.使用效率高的数据格式和解析方法。
通过测试发现,目前主流的数据格式,使用树形解析(如DOM)和流的方式解析(SAX)对比情况如下图所示,很明显,使用流的方式解析效率要高一些,
因为DOM解析是在对整个文档读取完后,再根据节点层次等再组织起来。而流的方式是边读取数据边解析,数据读取完后,解析也就完毕了。
在数据格式方面,JSON和Protobuf效率明显比XML好很多,XML和JSON大家都很熟悉,Protobuf是Google提出的,一种语言无关、平台无关、扩展性好的
用于通信协议、数据存储的结构化数据串行化方法。
从上面的图中我们可以得出结论就是尽量使用SAX等边读取边解析的方式来解析数据,针对移动设备,最好能使用JSON之类的轻量级数据格式为佳。
3、使用GZIP压缩方式下载数据,能减少网络流量;目前大部门网站都支持GZIP压缩,所以在进行大数据量下载时,尽量使用GZIP方式下载
HttpEntity entity = response.getEntity();
InputStream compressed = entity.getContent();
InputStream rawData = new GZIPInputStream(compressed);
4.其它一些优化方法:
回收java对象,特别是较大的java对像
对定位要求不是太高的话尽量不要使用GPS定位,可能使用wifi和移动网络cell定位即可。GPS定位消耗的电量远远高于移动网络定位。
尽量不要使用浮点运算
很多人开发的程序后台都会一个service不停的去服务器上更新数据,在不更新数据的时候就让它sleep,这种方式是非常耗电的,通常情况下,我们
可以使用AlarmManager来定时启动服务
最后一招,在运行你的程序前先检查电量,电量太低,那么就提示用户充电之类的
在android开发中使用http协议比较简单,sdk已经做了很好的封装了,具体使用方法可以参考我的这篇博文。而在游戏开发中,可以结合使用http和socket,
当然了http协议底层也是基于tcp协议的。http协议是无连接、无状态的,每次连接只能处理一个请求,然后就断了,而且发一个请求需要附加额外信息(请求行、
请求头),每次请求都需要重新建立连接;使用socket的好处是更高效和省流量,建立一次连接后,只要不手动或者出现异常断开,就可以一直互相发送数据,而
且是直接以字节的形式发送,不需要额外的附加信息,缺点就是难度加大了,需要服务端和客户端很好的配合,保证发送和读取时数据的顺序一致
57.IntentService?
IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中
的onHandleIntent(Intent)方法接收一个Intent对象,在适当的时候会停止自己(一般在工作完成的时候). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求
notify和notifyAll的区别?
线程的同步?
线程的同步是保证多线程安全访问竞争资源的一种手段
在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
当然这不是唯一控制并发安全的途径。
IOS与Android之间有什么区别?
Java中是如何体现面向对象思想的?
Java中是如何体现面向对象思想的?
Android中你是怎么实现多线程的?
SQLite的事务处理,添加删操作怎么进行优化
xml和json都做过,我还直接把自己曾经做的项目涉及到数据交互
你知道如何实现断点续传么,请你简单说一下
熟悉xmpp么
假如下载文件的时候,下载到一半突然间断了,怎么把资源回收?
熟悉java当中比较复杂的API么?具体调用了什么类和方法?比如说反射机制
Android连连看,你用到的是什么框架
8个球,其中一个球与其他7个球不同,给你一个天平,你如何在最短的时间里找到这个球
排序算法有哪些?你来演示一下快速排序的实现过程(画图),如果利用两个栈实现一个队列
String跟StringBuffer有什么区别,String是否能被继承,C跟Java有什么区别,Java回收机制,它是怎么实现的?怎么判断一个对象是不可达的?
侧滑
推送
常用的设计模式的应用场景有一定了解
项目经验
比如Android系统的性能管理,如何控制GC
网络知识不可少,http协议、tcp/ip协议、socket可能都需要去深入了解一下
(一)类的初始化顺序:
1.父类--静态变量
2.父类--静态初始化块
3.子类--静态变量
4.子类--静态初始化块
5.父类--变量
6.父类--初始化块
7.父类--构造器
8.子类--变量
9.子类--初始化块
10.子类--构造器
【注:静态变量和静态初始化块是依照他们在类中定义的顺序进行初始化的;同样变量和初始化块也遵循这个规律】
(二)创建了几个String对象
*常用的创建一个类的实例(对象)的方法有两种
1.使用new创建对象
2.调用Class类的newInstance方法,利用反射机制创建对象
但String特有的另一种创建对象的方式
3.引号内包括文本
String str="abc"
Java虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,如果已存在则不创建新的对象,直接返回已存在对象的
引用;如果没有则先创建这个对象,然后把它加入到字符串池中,再将他的引用返回。
【字符串常量池中对象的共享能够带来效率的提高,因此提倡大家用引号包括文本的方式来创建String对象;
常量的值在编译的时候就会被确定;】
*"=="可以用来比较两个变量
1.如果比较的是两个基本类型(char,byte,short,int,long,float,boolean,double),则是判断他们的值是否相等;
2.如果比较的是两个对象变量,则是判断他们的引用是否指向同一个对象;
(三)变量的覆盖
变量的值取决于我们定义的变量的类型,而不是创建的对象的类型;
【当变量类型是父类时,不管我们创建的对象是父类还是子类的,都不存在属性覆盖的问题;】
1.由于private受访问权限的限制,它不能被覆盖;
2.属性的值取父类还是子类的,并不取决于我们对象的类型(实际类型),而是取决于定义的变量的类型(静态类型);
3.friendly,protected,public修饰符并不影响属性的覆盖;
4.静态变量和静态常量属于类,因此它们不能被覆盖;
(四)final,finally,finalize的区别
final主要用于4个地方:
1.定义变量,包括静态的和非静态的;———— 如果修饰基本类型,表示这个变量被赋予的值是不可变的,即他是个常量;
如果修饰的是一个对象,表示这个变量被赋予的引用是不可变的;
2.定义方法的参数;
【综合1,2,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次】
*final变量和静态final变量未被初始时,编译会报错;
*用final修饰的变量比非fianl的变量拥有更高的效率,因此实际编程中应尽多的用常量来代替普通变量;
被final修饰的变量必须被初始化,初始化的方式有以下几种:
①在定义的时候初始化
②final变量可以在初始化块中初始化,不可以在静态初始化块中初始化
③静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始化
④final变量可以在类的构造器中初始化,但是静态final变量不可以
3.定义方法;———— 表示这个方法不可以被子类重写,但是它不影响它被子类继承;
4.定义类;———— final类不允许被继承,因此编译器在处理时把它所有的方法都当做final的,因此final类比普通类拥有更高的效率;
final类的方法不能被重写,因此不能用来修饰接口;
final类的方法不能被重写,但是final类的属性(变量)值是可变的,除非增加final属性,使属性值不可改变;
finally语句:
用在try/catch语句中,附带着一个语句块,表示这段语句最终总是会被执行;
1.return,continue,break语句都不能阻止finally语句块的执行;
2.finally语句块是在return方法退出之前,或循环被continue跳过,或循环被中断之前执行的;
finalize方法:
1.finalize()方法是一个属于object的方法,finalize()方法是GC垃圾回收运行机制的一部分;
2.finalize()方法是在GC清理它所从属的对象时被调用的,如果执行他的过程中出现了无法捕获的异常,GC将终止对该对象的清理,并且
该异常会被忽略;直到下一次GC开始清理这个对象时,他的finalize()会被再次调用;
3.system.gc()只是建议垃圾收集器GC启动,清理无用的对象释放内存空间,但是GC的启动是不一定的,由JAVA虚拟机来决定。
直到JAVA虚拟机停止运行时,有些对象的finalize()可能都没有运行过;
4.finalize()方法属于object类,object的任意子类都可以重写该方法,在其中释放系统资源或做其他的清理工作,如关闭输入输出流;
(五)传值还是传引用
JAVA中变量有以下两种:
1.基本类型变量,char, byte, short, int ,long, float, double, boolean;
2.引用类型变量,包括类,接口,数组(基本类型数组和对象数组);
*当基本类型的变量被当做参数传递给方法时,java虚拟机所做的工作是把这个值拷贝了一份,然后把拷贝的值传递到了方法的内部;
*当引用类型变量被当做参数传递给方法时,java虚拟机会拷贝一份这个变量所持有的引用,然后把它传递给java虚拟机为方法创建的局
部变量,从而这两个变量指向了同一个对象;
结论:
1.基本类型和基本类型变量被当做参数传递给方法时,是值传递,在方法实体中,无法给原变量再重新赋值,也无法改变他的值;
2.对象和引用型变量被当做参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它所指向对象的属性;
(六)字符串String杂谈
1.String的length()方法和数组的length属性;
2.中文汉字在char中的保存:
①java中一个char是两个字节,一个中文汉字是一个字符,GBK编码汉字是2个字节,UTF-8编码3个字节,所以通常来说,GBK编码char是可以保存汉字的;
一个英文字母是一个字节的,因此能够保存到一个byte,但是一个中文汉字不能保存到byte;
②字符串之间的“+”,效果是字符串的拼接;
字符之间的“+” ,效果等同于数字和数字之间,是一种数学运算;
3.字符串的反转输出
①取长度,for循环递减,反序得到charAt(i),连接字符串;
②new StringBuffer,然后使用StringBuffer的reverse自带函数反转;
4.按字节截取含有中文的字符串
①不同编码的中文字符,占有的字节数不同;GBK编码汉字2个字节,UTF-8编码汉字3个字节,UTF-16编码汉字4个字节;
②汉字截取了一半则无法正常显示,所以必须判断字符是否为汉字;可以根据如果当前字符的字节数>1,则说明字符为汉字;
因为所有编码的英文字符,占有1个字节;
③charAt(int index)是按照字符来分解字符串的;
substring(int beginIndex, int endIndex) 也是按字符截取的
(七)日期和时间的处理
(略)
(八)基本类型
1.基本类型都有对应的包装类,分为字符型,布尔类型,数值类型;
2.数值类型分为整数型byte,short,int,long 和浮点数类型float,double;
3.Java中的数值类型取值范围是固定的,不随机器硬件环境或操作系统改变,对于数值类型的取值范围,无需强制记忆,因为他们的值都以常量
的形式定义在对应的包装类中;
如 byte二进制位数:Byte.SIZE
byte最小值 :Byte.MIN_VALUE=-128
byte最大值 :Byte.MAX_VALUE=127
4.基本类型存储在栈中,因此它们的存储速度快于存储在堆中的对应包装类的实例对象;
几点结论:
①未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为double类型;
②如果一个整数的值超出了int类型能够表示的范围,则必须增加后缀"L",表示为long型;
③带有"F"后缀的整数和浮点数都是float类型的;带有"D"后缀的整数和浮点数都是double类型的;
④编译器会在编译期对byte,short,int,long,double,char型变量的值进行检查,如果超出了它们的取值范围就会报错;
⑤int型值可以赋给所有数值类型的变量;
long型值可以赋给long,float,double类型的变量;
float型值可以覆盖float,double类型的变量;
double型值只能赋给double类型的变量;
5.运算符对基本类型的影响
当使用+,-,*,/,%运算符对基本类型运算时,遵循如下规则
①只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,结果也是double类型;
②否则只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,结果也是float类型;
③否则只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,结果也是long类型;
当使用+=,-=,*=,/=,%=运算符对基本类型运算时,遵循如下规则
①运算符右边的数值首先被强制转换成与运算符左边数值相同的类型,然后再执行运算,且运算结果与运算符左边数值类型相同;
当使用==运算符在基本类型和其包装类对象之间比较时,遵循如下规则
①只要两个操作数中有一个是基本类型,就是比较他们的数值是否相等;
②否则就是判断这两个对象的内存地址是否相等,及是否是同一个对象;
6.Math.round()方法
7.switch语句
①byte,char,short,int四种基本类型以及他们的包装类都可以用于switch语句;
②long,float,double,boolean四种基本类型及他们的包装类都不能用于switch语句;
③enum枚举类型,可以用于switch语句,但是要在java 5.0(jdk 1.5)以上的版本才支持;
④所有类型的对象(包括String类,但在java 5.0/1.5以上版本中,排除byte,short,int,char四种基本类型对应的包装类)
都不能用于switch语句;
(九)继承、多态、重载、重写
继承和组合
①.继承给我们带来的好处就是对原有类的复用;类的复用可以提高我们的开发效率;
②.组合就是把原有的类定义为新类的一个属性,通过在新类中调用原有类的方法来实现复用;
③.使用继承和组合复用原有的类,都是一种增量式的开发模式,即不需要修改原有的代码;可以规避对原有代码修改而带来的风险;
多态
①多态是一种类型(都是car类型)表现出多种状态(宝马汽车状态BMW,奇瑞汽车状态cherryQQ)。
②方法的调用同方法所属的主题关联起来叫做绑定,分为前期绑定和后期绑定;
前期绑定:由编译器和连接程序实现,在程序运行之前进行绑定,又叫静态绑定;
后期绑定:由方法调用机制实现,在运行时根据对象的类型进行绑定,又叫动态绑定或运行时绑定;
③多态就是在后期绑定的机制上实现的,多态好处是消除类之间的耦合,使程序更容易扩展;
重载和重写
(略)
(十)多线程
*实现线程的两种方法
①继承Thread类,实现run()方法,将线程的执行主体放入其中;
②实现Runnable接口,实现run()方法,将线程的执行主体放入其中;
线程池:
①自己实现类:启动、停止、执行任务、关联任务队列等;
②java 5.0以上自己实现了线程池功能
————newCachedThreadPool创建动态线程池,线程数量会根据需要创建和回收;适合于执行短期大量任务;
————newFixedThreadPool创建包含固定数量线程对象的线程池;
————newSingleThreadExecutor在线程池中创建一个线程来执行所有任务;
三个方法都返回了一个ExecutorService类型的对象;该对象有两个方法submit(),shutdown()
————submit 接受任务,交于线程池中的线程去运行;
参数Runnable接口,实现run方法,无返回值;适合做操作,不关心结果;
参数Callable接口,实现call方法,call()将任务的执行结果作为返回值;适合于执行某种操作后,需要知道执行结果;
————shutdown 停止服务,否则所有线程会保持运行;
(十一)运算符
(略)
----------------------------------------------Java系列问题----------------------------------------------------
1.父类引用指向子类对象(Java中的多态)
class Father {
void print() { System.out.println("Father print"); }
int a=10;
}
class Son extends Father {
void print() { System.out.println("Son print"); }
void show() { System.out.println("Son show"); }
int a=20;
}
class Demo {
public static void main(String args[]) {
Father obj = new Son();
obj.print(); // 调用的是 Son print
obj.show(); // 这个调用会报错!
obj.a; // 调用的是Father中的a,值为10
}
}
Father obj = new Son(); 编译看左边(静态类型)
运行看右边(实际类型)
obj.print(); // 调用的是 Son print * 方法的接收者以实际类型为判定依据
obj.a; // 调用的是Father中的a,值为10 * 变量的值以静态类型为判定依据
* 因为多态是针对方法的重写
1.当出现“父类引用指向子类对象”的情况时,如果子类中重写了父类中的一个方法,那么父类引用在调用这个方法的时候,将会调用子类中
的这个方法(动态绑定、执行);
2.如果你想实现多态,那么必须有三个条件:父类引用,子类对象,方法覆盖;
这里,如果Fathor类有一个show()方法,那么形成方法覆盖,此时才可以这么写:obj.show()——此刻形成了多态。
3.在没有方法覆盖的情况下,用父类引用去访问一个子类的方法,如上面的show()方法——由于父类引用没有这么大范围的权限,所以会报错;
4.父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的(对于子类中的成员变
量,它也是无法调用的);另外,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
【定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。】
5.静态方法是没有多态性的:静态方法不会被子类所覆盖,因此,父类引用只能调用父类的静态方法。
附:一些问题的解答
A:当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员
Q:为什么这样不可以?因为f是FatherClass,所以编译器只知道f拥有FatherClass.class的信息,FatherClass.class以外的信息,编译器
不知道,而子类的对象成员是在SonClass.class里,也就是说在FatherClass.class以外,所以f无法访问子类的对象成员。
2.Java的异常和错误
Java异常可分为3种:
(1)编译时异常:Java.lang.Exception
(2)运行期异常:Java.lang.RuntimeException
(3)错误:Java.lang.Error
* 编译时异常指的是我们必须在代码中显示的处理,或者try或者throw,处理完成后才能编译成功,常见的是IOException, InterruptedException。
* 运行期异常指的是我们写的代码可以编译通过,但是如果运行时出现问题,则会出现运行期异常,最常见的就是NullPointerException、
IndexOutOfBoundsException、ClassNotFound、ResourceNotFound,0被除,这类异常需要更改程序来避免。
* 运行时异常也可以通过捕捉预处理,因为运行时异常如果足够仔细,代码写的足够健壮,完全可以避免;
* Java.lang.Exception和Java.lang.Error继承自Java.lang.Throwable;
Java.lang.RuntimeException继承自Java.lang.Exception.
* Error是错误,可能源于程序的bug,但一般更可能源于环境问题, 出了Error程序就挂了,最常见的就是OutOfMemoryError。
3.编码问题(UTF-8、gb2312、unicode)
A:字符集(CharacterSet)?
Q:字面上的理解就是字符的集合,例如ASCII字符集,定义了128个字符;GB2312定义了7445个字符。而计算机系统中提到的字符集准确来说,指
的是已编号的字符的有序集合(不一定是连续)。
Q:Unicode是两个字节吗?
A:Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储为什么样的字节流,取决于字符编码方案。
推荐的Unicode编码是UTF-16和UTF-8。
Q:带签名的UTF-8指的是什么意思?
A:带签名指的是字节流以BOM标记开始。很多软件会“智能”的探测当前字节流使用的字符编码,这种探测过程出于效率考虑,通常会提取字节流前
面若干个字节,看看是否符合某些常见字符编码的编码规则。由于UTF-8和ASCII编码对于纯英文的编码是一样的,无法区分开来,因此通过在
字节流最前面添加BOM标记可以告诉软件,当前使用的是Unicode编码,判别成功率就十分准确了。但是需要注意,不是所有软件或者程序都能
正确处理BOM标记,例如PHP就不会检测BOM标记,直接把它当普通字节流解析了。因此如果你的PHP文件是采用带BOM标记的UTF-8进行编码的,
那么有可能会出现问题。
Q:Unicode编码和以前的字符集编码有什么区别?
A:早期字符编码、字符集和代码页等概念都是表达同一个意思。例如GB2312字符集、GB2312编码,936代码页,实际上说的是同个东西。但是对于
Unicode则不同,Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码,则是对UTF-8、UCS-2/UTF-16等具体编码方案的统称而已,
并不是具体的编码方案。所以当需要用到字符编码的时候,你可以写gb2312,codepage936,utf-8,utf-16,但请不要写unicode(看过别人
在网页的meta标签里头写charset=unicode,有感而发)。
(1)GB2312以及GBK字符集,限定了使用最多2个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查表,也就是通过代码页就可
以直接将字符映射为存储设备上的字节流了
(2)“虽然每个字符在Unicode字符集中都能找到唯一确定的编号(字符码,又称Unicode码),但是决定最终字节流的却是具体的字符编码”
(3)unicode是包含了gb2312中的字符集,但是每种编码解码方式不一样,也就是说,utf-8编码是一种规则,他自己编码的字节流,需要用自己的规则
去解码,utf-8是变长编码的,它不能按照gb2312那样解析字节流。所以如果不一致,就会出现乱码情况
4.static作用?
①可修饰变量(属性);
————Static int data 语句说明data为类变量,为一个类的共享变量,属于整个类。
②可修饰代码块
————当static修饰代码块时(注:此代码块要在此类的任何一个方法之外)那么这个代码块在代码被装载进虚拟机生成对象的时候可被装载
一次,以后再也不执行了。一般静态代码块被用来初始化静态成员。
③可修饰方法;
————Public static void printData(){}
表明此类方法为类方法(静态方法)。静态方法不需要有对象,可以使用类名调用。静态方法中不允许访问类的非静态成员,包括成
员的变量和方法,因为此时是通过类调用的,没有对象的概念。This.data是不可用的。
一般情况下,主方法是静态方法,所以可调用静态方法,主方法为静态方法是因为它是整个软件系统的入口,而进入入口时系统中没
有任何对象,只能使用类调用。
5.JAVA类加载机制
使用使用java编译器可以把JAVA代码编译为存储字节码的Class文件,Class文件是二进制字节流;
虚拟机不关心Class的来源什么,只要符合Class文件的结构就可以在java虚拟机中运行;即语言无关性;如JRuby\Jython等都可以
编译出Class字节码文件,在JAVA虚拟机上运行;
Class文件的结构:二进制流,各项数据紧凑排列,如魔数(头4个字节,文件格式),版本号(5~6字节),常量池(字面量、符号
引用),访问标志等;
JDK的bin目录下使用专门用于分析Class文件字节码的工具:javap,可以查看编译器生成的字节码;
总之,Class文件是Java虚拟机执行引擎的数据入口;Class文件中描述的各种信息,最终都需要加载到虚拟机中才能被运行和使用;
虚拟机把Class文件加载到内存、进行校验、转换解析和初始化,最终形成被虚拟机直接使用的Java类型,这就是虚拟机的加载机制;
动态加载的机制,提供大家一个方法: 在命令行窗口运行Java程序的时候,加上这个很有用的参数:
java -verbose *.class
这样会清晰的打印出被加载的类文件,大部分是jdk自身运行需要的,最后几行会明显的看到自己用到的那几个类文件被加载进来的顺序。即
使你声明了一个类对象,不实例化也不会加载,说明只有真正用到那个类的实例即对象的时候,才会执行加载。
虚拟机中类加载器:通过一个类的全限定名来获取描述此类的二进制字节流,及class文件;
虚拟机执行的步骤:
1.虚拟机有一个用于类加载的机制,用于从数据源中读取类文件,这个源可以是磁盘文件或者web上的文件,这里假设要加载的是TestLoader。
2.如果TestLoader这个类拥有类型为其他类的实例或者是某个类的子类,也就是说它和其他类产生关联或者依赖,那么相关的类也会被加载
3.虚拟机执行TestLoader中的main方法
4.如果main方法中有对其他类的依赖,那么加载相应的类。
可见联系虚拟机和我们的程序的“中间人”就是类加载器,用什么就加载什么。类加载器是有其自身的体系的。每一个java程序至少拥有三个类加载器:
1.引导类加载器:负责加载系统类,通常从rt.jar中加载,jre/lib目录;
2.扩展类加载器:用于从jre/lib/ext目录加载扩展类。
3.系统类加载器:用于加载应用类,可以找到classpath里面指定的jar中的类。
类加载器的层次结构
类加载器有一个等级结构,可以认为默认的是上层优先的原则。除了引导类加载器外其他的类加载器都有一个父类加载器。根据规定,每个类加
载器都会先为其父类加载器提供工作机会,如果父类不能完成此项任务,他才会自己去做。
研究类的加载有助于了解JVM执行过程,让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。
一、简单过程
Java程序运行的场所是内存,当在命令行下执行:
java HelloWorld 命令的时候,JVM会将HelloWorld.class加载到内存中,并形成一个Class的对象HelloWorld.class。其中的过程就是类加载过程:
1、寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。
4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。
5、最后由AppClass Loader加载HelloWorld类。
以上就是类加载的最一般的过程。
类加载器的特点:
1、运行一个程序时,总是由AppClass Loader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3、Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
类加载有三种方式:
1、命令行启动应用时候由JVM初始化加载
2、通过Class.forName()方法动态加载
3、通过ClassLoader.loadClass()方法动态加载
同一个ClassLoader加载的类文件,只有一个Class实例。但是,如果同一个类文件被不同的ClassLoader载入,则会有两份不同的ClassLoader实例
6.关于寻找class文件原理??
建议大家在入门的时候在命令行窗口编译和运行,不要借助JCreator或者Eclipse等IDE去帮助做那些事情。尝试自己这样做:
javac -classpath yourpath *.java
java -classpath yourpath *.class
也许很多人都能看懂,设置classpath的目的就是告诉编译器去哪里寻找你的class文件. JVM去查询类的原理,编译器加载类要依靠classloader,
而classloader有3个级别,从高到低分别是BootClassLoader(名字可能不准确) , ExtClassLoader, AppClassLoader.
这3个加载器分别对应着编译器去寻找类文件的优先级别和不同的路径:
BootClassLoader对应jre/classes路径,是编译器最优先寻找class的地方
ExtClassLoader对应jre/lib/ext路径,是编译器次优先寻找class的地方
AppClassLoader对应当前路径,所以也是编译器默认找class的地方
其实大家可以自己写个程序简单的测试,对任何class,例如A,
调用new A().getClass().getClassLoader().toString() 打印出来就可以看到,把class文件放在不同的路径下再次执行,就会看到区别。
特别注意的是如果打印出来是null就表示到了最高级 BootClassLoader, 因为它是C++编写的,不存在Java对应的类加载器的名字。
寻找的顺序是一种向上迂回的思想,即如果本级别找不到,就只能去本级别之上的找,不会向下寻找。
这样希望大家不至于迷惑为什么总报错找不到类文件,不管是自己写的还是导入的第三方的jar文件(J2ee中经常需要导入的)。
7.Java反射机制
Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。
Java可以加载一个运行时才得知名称的class,获得其完整结构
Java反射机制提供如下功能:
————在运行时判断任意一个对象所属的类
————在运行时构造任意一个类的对象
————在运行时判段任意一个类所具有的成员变量和方法
————在运行时调用任一个对象的方法
————在运行时创建新类对象
————在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。
8.JAVA的内存管理
A:
————Java的内存管理就是对象的分配和释放问题。
————在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数
来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。
Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了
能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了
更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。
在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。
JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。
————在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点:
首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;
其次,这些对象是无用的,即程序以后不会再使用这些对象。
如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存
典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,
这些引用会自动被清理。
内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。
eg:
某个(某些)对象已经不在被使用应该被gc所回收,但有一个对象持有这个对象的引用而阻止这个对象被回收。比如我 们通常会这样创建一个
View TextView tv = new TextView(this);这里的this通常都是Activity。所以这个TextView就持有着这个Activity的引用
通常情况下,当用户转动手机的时候,android会重新调用OnCreate()方法生成一个新的Activity,原来的 Activity应该被GC所回收。但如
果有个对象比如一个View的作用域超过了这个Activity(比如有一个static对象或者我们把这个 View的引用放到了Application当中),这时候原
来的Activity将不能被GC所回收,Activity本身又持有很多对象的引用,所以 整个Activity的内存被泄漏了。
经常导致内存泄漏的一些原因:
1,一个View,的作用域超出了所在的Activity的作用域,比如一个static的View或者 把一个View cache到了application当中 etc
2,某些与View关联的Drawable的作用域超出了Activity的作用域。
3,Runnable对象:比如在一个Activity中启用了一个新线程去执行一个任务,在这期间这个Activity被系统回收了, 但Runnalbe的任务还
没有执行完毕并持有Activity的引用而泄漏,但这种泄漏一般来泄漏一段时间,只有Runnalbe的线程执行完闭,这个 Activity又可以被正常回收了。
4,内存类的对象作用域超出Activity的范围:比如定义了一个内存类来存储数据,又把这个内存类的对象传给了其它Activity 或者Service等。
因为内部类的对象会持有当前类的引用,所以也就持有了Context的引用。解决方法是如果不需要当前的引用把内部类写成 static或者,把内部类
抽取出来变成一个单独的类,或者把避免内部对象作用域超出Activity的作用域。
out Of Memery Error 在android中每一个程序所分到的内存大小是有限的,如果超过了这个数就会报Out Of Memory Error。android给程序分
配的内存大小与手机硬件有关,以下是一些手机的数据:
G1:16M Droid:24 Nexus One:32M Xoom:48Ms
所以尽量把程序中的一些大的数据cache到本地文件。以免内存使用量超标。
9..Java和C++的区别
A:
————速度:解释过的Java要比C的执行速度慢上约20倍
Java是运行在JVM中的,并且是编译成JVM可识别加载的.class,并不是完全编译成计算机直接可执行的程序
————JAVA没有结构、联合,只有“类”(Class)!
————Java的“主类型”(Primitive type),boolean,char,byte,short,int,long,float以及double。所有主类型的大小都是固有的,
且与具体的机器无关(考虑到移植的问题)。
————Java的主类型都只能在堆栈上创建,Java的对象都从堆中分配
————Java没有象C和C++的指针,用new创建一个对象的时候,会获得一个引用
————JAVA有垃圾回收机制,c++用析构函数回收垃圾。
————JAVA的应用在高层,C++在中间件和底层
————Java不提供多重继承机制,但Java提供了一个interface关键字
10.泛型
A:
————泛型是在java5才加入的,简单的讲就是对集合中的类型进行约束,也增强了对象的安全。
————泛型就是给数据集合强制限定了存储数据的类型。是在编译期间约束类型的,保证对象安全。
比如 ArrayList<String>a=new ArrayList<String>();
上面这个ArrayList里只能存储String类型的数据,如果你往里放Integer的话就会报错,而且取出数据的时候也是String,不需要强制转换类型。
————在java5之前所有放进数据集合中的数据都会被转成object对象存储,而且不管什么时候都可以存的进去,你可以先放一个String然后再放一个
Integer这样也能编译通过,当取出数据的时候,取出都是object类型的数据,必须进行强制类型转换转换成原来的数据类型才能使用。
11.HashMap遍历的两种方式,推荐使用entrySet()
A:
————HashMap的遍历有两种常用的方法,那就是使用 entryset 及 keyset 来进行遍历,但两者的遍历速度是有差别的;
keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value; ——效率低,尽量少使用
entryset只是遍历了第一次,他把key和value都放到了entry中; ——效率高,推荐使用此种方式
(1)第一种:
Map map=new HashMap();
Iterator iter=map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry=(Map.Entry)iter.next();
Object key=entry.getKey();
Object val=entry.getValue();
}
(2)第二种
Map map=new HashMap();
Iterator iter=map.keyset.iterator();
while(iter.hasNext()){
Object key=iter.next();
Object val=map.get(key);
}
为什么差别这么大呢?这两种遍历方式代码几乎完全雷同,只是在获取Value对象的时候不一样。
————entrySet遍历方式获取Value对象是直接从Entry对象中直接获得,时间复杂度T(n)=o(1);
————keySet遍历获取Value对象则要从Map中重新获取
keySet遍历Map方式比entrySet遍历Map方式多了一次循环,多遍历了一次,当Map的size越大时,遍历的效率差别就越大
12.HashMap与HashTable的区别
(1)hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
(2)hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
(3)hashMap允许空键值,而hashTable不允许。
13.List、Map、Set三个接口,存取元素时,各有什么特点?
List 以特定次序来持有元素,可有重复元素。Set 无法拥有重复元素,内部排序。Map 保存key-value值,value可多值
14.HTTPS和HTTP的区别
A:
HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议
它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,
单来说它是HTTP的安全版。 它是由Netscape开发并内置于其浏览器中,用于对数据进行压缩和解压操作,并返回网络上传送回的结果。
HTTPS实际上应用了Netscape的安 全全套接字层(SSL)作为HTTP应用层的子层。(HTTPS使用端口443,而不是象HTTP那样使用端口80
来和TCP/IP进行通信。)SSL使 用40 位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。HTTPS和SSL支持使用X.509数字
认证,如果需要的话用户可以确认发送者是谁。
HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全
HTTP代理:
HTTP代理是基于TCP的socket连接,就是A无法直接连接C,但B即可以连接A,也可以连接C
那么需要B开启他的代理服务,设置服务器并监听端口。
A连接B的80或者8080代理服务器端口,建立socketAB,发送(连接C的WEB请求)到B,B收到该请求后进行解析,然后B去连接C,建立socketBC,
并通过socketBC把(连接C的WEB请求)发送给C。
反之依然。
也就是说TCP层知道是A->B->C,但是HTTP封装的包只知道是A->C
hash:
Hash,一般翻译做“散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长
度的输出,该输出就是散列值。
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.
也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系
Hash算法是根据目标数据的内容生成一个完全依赖于目标数据内容的字节序列,同样内容的数据,经过Hash算法,生成同样的字节序列。
Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。
反射
————反射就是让你可以通过名称来得到对象 ( 类,属性,方法 ) 的技术;
例如我们可以通过类名来生成一个类的实例;知道了方法名,就可以调用这个方法;知道了属性名就可以访问这个属性的值。
java对象锁
A:
————在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。
————synchronized对象的锁:
所有对象都自动含有单一的锁。
当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。
三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象
synchronized (this)),所以对它们的方法依然是互斥的。
三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,这些同步块处在不同的方法中,并且是同步到三个不同的对象(synchronized
(this),synchronized(syncObject1),synchronized (syncObject2)),所以对它们的方法中的临界资源访问是独立的。
————Lock对象锁
为什么一般要用http来进行网络编程,而用 socket 的比较少? sockest有什么缺点?
A:
————TCP或者UDP协议其实都是基于Socket来实现的;
————http在tcp之上,要先用socket建立tcp才能实现http。http只不过帮你把底层实现屏蔽了,面向对象的特点。所以Socket效率高些.
HTTP协议:超文本传输协议,是一种通信协议(如FTP、Telnet、SMTP、HTTP、RIP),如对应于应用层 ,HTTP协议是基于TCP连接的
————socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层;
————两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现,是以不同的应用层协议来定义的`如HTTP`FTP`...
socket是对端口通信开发的工具,它要更底层一些
TCP和UDP的区别:
————TCP是面向链接的,虽然说网络的不安全不稳定特性决定了多少次握手都不能保证连接的可靠性,但TCP的三次握手在最低限度上保证了连接的可靠性;
————UDP不是面向连接的,UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,
所以说UDP是无连接的、不可靠的一种数据传输协议。
————UDP的开销更小数据传输速率更高,因为不必进行收发数据的确认,所以UDP的实时性更好。
————知道了TCP和UDP的区别,就不难理解为何采用TCP传输协议的MSN比采用UDP的QQ传输文件慢了,但并不能说QQ的通信是不安全的,因为程序员可以手动
对UDP的数据收发进行验证,比如发送方对每个数据包进行编号然后由接收方进行验证啊什么的,即使是这样,UDP因为在底层协议的封装上没有采用类
似TCP的“三次握手”而实现了TCP所无法达到的传输效率。
HTTP通讯的get和post
A:
GET和POST
————HTTP协议中的两种不同的请求方式——GET和POST。
————GET方式在进行数据请求时,会把数据附加到URL后面传递给服务器,比如常见的:http://XXX.XXX.XXX/XX.aspx?id=1,
————POST方式则是将请求的数据放到HTTP请求头中,作为请求头的一部分传入服务器。所以,在进行HTTP编程前,首先要明
确究竟使用的哪种方式进行数据请求
----------------------------------------------Android系列问题----------------------------------------------------
1.Android的activity启动的几种模式简介。
A:
————launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity
实例,是否和其他Activity实例公用一个task里。
————Activity一共有以下四种launchMode:standard / singleTop / singleTask/ singleInstance
可以在AndroidManifest.xml配置<activity>的android:launchMode属性为以上四种之一
standard模式是默认的启动模式,不用为<activity>配置android:launchMode属性即可,当然也可以指定值为standard。
每次跳转系统都会在task中生成一个新的FirstActivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的FirstActivity实例。
【standard启动模式:不管有没有已存在的实例,都生成新的实例】
【singleTop启动模式:如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例】
【singleTask模式:如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前】
【singleInstance模式:启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入】
2.如何区分activity/task/process
A:
————task是一个具有栈结构的对象,可以理解为一个栈。一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。
————process,一个应用里面可以有多个PId,一个pid对应一个进程,每次打开是系统都会赋予不同的pid,但是uid貌似是不变的;
【android为每个应用几乎都分配了不同的UID,在android中PID,和UID都是用来识别应用程序的身份的;
但UID是为了不同的程序来使用共享的数据: ①通过UID共享数据,在manifest配置相同的android:sharedUserId;
②不同的程序能够相互访问,还需要拥有相同的签名;】
ActivityManager类可以获取运行信息,如下:
getRecentTasks() 最近开的task,HOME键长按会看到这个
getRunningAppProcesses() 运行中的作为app容器的process
getRunningServices() 运行中的后台服务
getRunningTasks() 运行中的任务
3.Android 多个APK数据共享
A:
Android给每个APK进程分配一个单独的用户空间,其manifest中的userid就是对应一个Linux用户,所以不同APK(用户)间互相访问数据默认是禁止的.
但是它也提供了2种APK间共享数据的形式:
————Share Preference. / Content Provider
APK可以指定接口和数据给任何其他APK读取. 需要自己实现接口和Share的数据.
————Shared User id
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也
可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样.
4.android内存管理的原理
Android采取了一种有别于Linux的进程管理策略,有别于Linux的在进程活动停止后就结束该进程,Android把这些进程都保留在内存中,直到系
统需要更多内存为止。这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度。
那Android什么时候结束进程?结束哪个进程呢?
Android将进程分为六大类,对进程的重要性进行评估:
————1.前台进程(foreground):目前正在屏幕上显示的进程和一些系统进程。
————2.可见进程(visible):可见进程是一些不再前台,但用户依然可见的进程,
————3.次要服务(secondary server):目前正在运行的一些服务
————4.后台进程(hidden):就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。一旦我们按home返回主界面(注意
是按home,不是按back),程序就驻留在后台,成为后台进程
————5.内容供应节点(content provider):没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。
————6.空进程(empty):有些程序,在程序退出后,依然会在进程中驻留一个空进程,进程里没有任何数据在运行,作用往往是提高该程序下
次的启动速度或者记录程序的一些历史信息。
5. 如何优化UI
解析布局文件花费的时间取决于布局的复杂性:资源数据越大解析越慢
1. 使用相对布局代替线性布局或其他布局
2. 合并布局,如果自己布局的顶层元素是一个FrameLayout,可以使用<merge />标签来合并布局
3. 重用布局,使用 <include />
有两个目的:1. 多次使用相同的布局, 2. 布局有一个通用的组成部分,或有部分依赖于设备配置
4. ViewStub
ViewStub是轻量级且不可见的视图,当需要时,在自己的布局中可以用它来推迟展开布局。
布局优化工具,可以使用HierarchyViewer和layoutopt,这两个工具都位于SDK的tools目录下
6.说说应用saveInstance 与 restoreInstance区别,如何使用?
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,
它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()才会被调用。
但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需
要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建
后恢复缓存的数据。可以重写Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。
7.android view,surfaceview,glsurfaceview的区别:
SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使
用SurfaceView;
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面;而View必须在UI的主线程中更新画面。
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,
触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一
下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类。
1)被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。因为这种情况下,
这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
2)主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需
要surfaceView来控制。
8.shareUserId简介。
A:
Android给每个APK进程分配一个单独的空间,manifest中的userid就是对应一个分配的Linux用户ID,并且为它创建一个沙箱,以防止影响
其他应用程序(或者其他应用程序影响它)。用户ID 在应用程序安装到设备中时被分配,并且在这个设备中保持它的永久性。
通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,在保障了程序运行的稳定。然后
在有些时候,我们自己开发了多个APK并且需要他们之间互相共享资源,那么就需要通过设置shareUserId来实现这一目的。
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也可以配置成
运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。
9..Activity跳转流程
A:
————Activity从Main跳转到Subscreen过程函数调用顺序:
Main.onCreate()→Main.onStart()→Main.onResume()→Main.onPause()→Subscreen.onCreate()→Subscreen.onStart()→Subscreen.onResume()
→Main.onStop()
————Android在运行时,正常情况下,Activity的切换其实是将运行过的Activity压入栈中,每创建一个Activity就向栈中压入该Activity;
当点击返回键的时候会销毁当前Activity,即将当前Activity从栈顶删除,接着显式栈中的第二个Activity(也即现在的栈顶Activity)。
【注意:点击返回键是会destroy掉当前Activity的,而不是再将该Activity压入栈里】
【后台键home——activity onstop; 返回键back——activity onDestroy】
————Activity跳转,onPause,onStop差别,当之前Activity切换到后台,但是仍然可见的时候,执行onPause;
当之前Activity切换到后台,而且完全不可见时候,执行onStop;
10.在android中,请简述jni的调用过程?
A:
1)安装和下载Cygwin,下载 Android NDK
2)在ndk项目中JNI接口的设计
3)使用C/C++实现本地方法
4)JNI生成动态链接库.so文件
5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可
11.如何退出Activity?如何安全退出已调用多个Activity的Application?
A:
————单一Activity的应用来说,退出很简单,直接finish()即可。也可以用killProcess()和System.exit()这样的方法。
————多Activity的应用来说,记录打开的Activity, 在需要退出时,关闭每一个Activity即可;
或者发送特定广播,在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可;
12.如何启用Service,如何停用Service
A:
————service一般没有用户操作界面,它运行于系统中不容易被用户发觉,实现步骤如下
第一步:继承Service类 public class BluetoothConnController extends Service{}
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置: <service android:name=".DemoService" />
第三步:Context.startService()和Context.bindService
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,
但是它们的使用场合有所不同。
1.使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用
服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
2.采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
3.采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个
时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()
方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)
。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
————Service的生命周期
1.Service常用生命周期回调方法如下:
onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onDestroy()该方法在服务被终止时调用。
2. Context.startService()启动Service有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽
管不会多次创建服务,但onStart() 方法会被多次调用。
3. Context.bindService()启动Service有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次
调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
备注:
1. 采用startService()启动服务
Intent intent =new Intent(DemoActivity.this, DemoService.class);
startService(intent);
2.Context.bindService()启动
Intent intent =new Intent(DemoActivity.this, DemoService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//unbindService(conn);//解除绑定
13.请解释下在单线程模型中Message、Handler、MessageQueue、Looper之间的关系 ???
A:
Handler简介:
一个Handler允许你发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联。每一个线程实例和一个单独的线程以及该线程
的MessageQueue相关联。当你创建一个新的Handler时,它就和创建它的线程绑定在一起了。这里,线程我们也可以理解为线程的MessageQueue。从这一
点上来看,Handler把Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler负责执行他们。
Handler有两个主要的用途:
(1)确定在将来的某个时间点执行一个或者一些Message和Runnable对象。
(2)在其他线程(不是Handler绑定线程)中排入一些要执行的动作。
postDelayed\sendMessage等操作把Message对象加入MessageQueue,这些Message对象包含一些信息,Handler的hanlerMessage(Message)会处理这些
Message.当然,handlerMessage(Message)必须由Handler的子类来重写。这是由编程人员实现。
Message简介:
Message类就是定义了一个信息,这个信息中包含一个描述符和任意的数据对象,这个信息被用来传递给Handler.Message对象提供额外的两个int域和
一个Object域,这可以让你在大多数情况下不用作分配的动作。
尽管Message的构造函数是public的,但是获取Message实例的最好方法是调用Message.obtain(),或者Handler.obtainMessage()方法,这些方法会从
回收对象池中获取一个。
MessageQueue简介:
包含message列表的底层类。Looper负责分发这些message。Messages并不是直接加到一个MessageQueue中,而是通过MessageQueue.IdleHandler关联到
Looper。你可以通过Looper.myQueue()从当前线程中获取MessageQueue。
Looper简介:
Looper类被用来执行一个线程中的message循环。默认情况,没有一个消息循环关联到线程。在线程中调用prepare()创建一个Looper,然后用loop()
来处理messages,直到循环终止。
???待完善修改
14.AIDL的全称是什么?如何工作?能处理哪些类型的数据?
A:
AIDL的英文全称是Android Interface Define Language,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
icp:interprocess communication :内部进程通信
【java中RMI介绍
RMI:(远程接口调用)
1. RMI的原理:
RMI系统结构,在客户端和服务器端都有几层结构。
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传
输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程
引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的远程
对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传
输层和远程调用层返回。最后,占位程序获得返回值。
2. RMI(远程方法调用)的组成
一个正常工作的RMI系统由下面几个部分组成:
远程服务的接口定义
远程服务接口的具体实现
桩(Stub)和框架(Skeleton)文件
一个运行远程服务的服务器
一个RMI命名服务,它允许客户端去发现这个远程服务
类文件的提供者(一个HTTP或者FTP服务器)
一个需要这个远程服务的客户端程序
】
当B进程要去调用A进程中的service时,并实现通信,我们通常都是通过AIDL来操作的。建立AIDL服务步骤:
————A工程:
①Java包目录中创建一个RemoteService.aidl文件,在里面我们自定义一个接口,含有方法get。
【注:AIDL规定,文件名必须和interface XXX名字相同,否则会报错】
如果aidl文件的内容是正确的,ADT插件会在gen目录下自动生成一个Java接口文件(*.java),RemoteService.java文件,该类中含有一个名为
RemoteService.stub的内部类(远程服务的本地代理类,Stub对象是在被调用端进程,也就是服务端进程),该内部类中含有aidl文件接口的get方法。
②然后定义自己的MyService类;
1.在MyService类中自定义一个内部类去继承RemoteService.stub这个内部类,实现get方法。
/* 该类是MyService的内部类
* 该类继承了RemoteService.stub类而不是extends Binder类。
* RemoteService.stub是Binder的子类。
* 进程内的Service定义MyBinder内部类是继承Binder类。
*/
public class MyBinder extends RemoteService.stub {
@Override
public String get() throws RemoteException {
return MyService.this.get();
}
}
private String get() {
String str="MyService 自定义 get()...";
return str;
}
2.MyService类需要实现onBind方法,在onBind方法中返回这个内部类的对象,系统会自动将这个对象封装成IBinder对象(即步骤3中的IBinder serviceXXX),
传递给他的调用者。
private MyBinder mBinder;
public IBinder onBind(Intent intent) {
Log.i(tag, "...service onBind()...");
return mBinder;
}
3.需要在AndroidManifest.xml文件中配置MyService类,代码如下:
<!-- 注册服务 -->
<service android:name=".MyService">
<intent-filter>
<!-- 指定调用AIDL服务的ACTION -->
<action android:name="net.blogjava.mobile.aidlservice.RemoteService" />
</intent-filter>
</service>
指定调用AIDL服务的ACTION,如果其他进程知道这个ACTION,MyService这个类能够被别的进程访问,有了这个ACTION,B工程才能找到A工程实现通信。
————B工程:
①将A工程中生成的RemoteService.java文件拷贝到B工程中
②调用bindService方法绑定aidl服务,
bindService(newIntent("net.blogjava.mobile.aidlservice.RemoteService"),serviceConnection, Context.BIND_AUTO_CREATE);
第一个参数 Intent , 用AIDL服务的ACTION创建INTENT
注意<action>标签中的android.name 的属性值就是客户端要引用该服务的ID,也就是Intent.setAction("xxxxxxx");(或者Intent构造方法的参数)。
注意,这里在Client进程中不能通过new Intent(context,IMyService.class) 来启动
第二个参数ServiceConnection,即执行跨进程绑定服务。
在客户端的onServiceConnected方法中用IMyService.Stub.asInterface()方法转换服务器端传递过来的IBinder对象;
获得从服务中返回的对象后,通过调用该对象中的方法或属性值达到与被绑定的服务交换数据和控制该服务的目的
RemoteService mService; //接口的一个引用
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log("connect service");
mService = RemoteService.Stub.asInterface(service); //获得另一个进程中的Service传递过来的IBinder对象-mService
try {
mService.registerTestCall(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
Log("disconnect service");
mService = null;
}
};
15.什么是ANR 如何避免它?
A:
————ANR:Application Not Responding,应用无响应
在Android里,ActivityManager(活动管理器)和WindowManager(窗口管理器)这两个系统服务监控着应用程序的响应能力。Android会在检测到
以下情形中之一时,弹出ANR对话框:
1.未在5秒内对用户输入事件响应; ———— 对输入事件(如按键、触摸屏事件)的响应超过5秒
2.BroadcastReceiver未在10秒内执行完 ————
————Android应用默认运行在单线程里,叫UI线程或主线程。应用所有工作都在UI线程里,如果花费很长时间才能完成,会触发ANR,因为此时应用无法
操控输入事件或广播。
因此,UI 线程里的任何方法都应该尽可能地做轻量的工作,特别是Activity在生命周期方法,像onCreate(),onResume().潜在的耗时操作,像网络,
数据库,或昂贵的计算(像改变图片大小)应该在工作线程里完成(或者在数据库操作案例里,通过一个异步请求)。
但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束,不需要调用Therad.wait()或者Thread.sleep()方法,取而代之的是,主线程为
子线程提供一个句柄(Handler),让子线程在即将结束的时候调用它,能够保证程序对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处
理而产生的ANR。
【特别强调BroadcastReceiver的执行时间,执行密集任务应该用startService启动IntentService,IntentService在oncreat中会创建一个异步线程】
16.如何将SQLite数据库(dictionary.db文件)与apk文件一起发布?
A:
————assets目录与res/raw 目录的区别
1.assets目录与res/raw两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
res/raw不参加编译的资源,(已经建好的数据库,图片等),可以用于在程序首次运行时,直接复制到sd卡上
2.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;
assets文件夹下的文件不会被映射到R.java中,读取assets目录下的文件必须指定文件的路径,访问的时候需要AssetManager类。
3.读取文件资源:
读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作 InputStream is = getResources().openRawResource(R.id.filename);
读取assets下的文件资源,通过以下方式获取输入流来进行写操作 AssetManager am = getResources().getAssets();
InputStream is = am.open("filename");
————可以将dictionary.db文件复制到Android工程中的res/raw目录中。所有在res/raw目录中的文件不会被压缩,这样可以直接提取该目录中
的文件。
17.如何打开res/raw目录中的数据库文件?
A:
无论是raw文件夹还是assets文件夹,都是在生成apk的时候不编译而直接携带在apk的压缩包中的,这可以打开apk检验;
提取的方法都是从inputstream转,转成什么形式的,要看对inputstream的操作;基本方法是使用getResources().openRawResource方法
获得res aw目录中资源的 InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。
在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。
raw & assets资源比较重要的一点,就是文件的大小限制,单个文件的大小不可以大于1M。
解决的方法是: 将文件split为小于1M的文件,在读取的时候outputstream不要close,而是合并写这些文件,最后就得到原始文件。
18.android中的动画有哪几类,它们的特点和区别是什么?
A:
————两种,一种是Tween动画、还有一种是Frame动画。
Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;
Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。
19.handler机制的原理? ???
A:
andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。
1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
20.DDMS和TraceView的区别? ???TraceView
A:
DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,
TraceView是程序性能分析器 。
21.谈谈Android的IPC(进程间通信)机制
A:
————IPC是内部进程通信的简称, 是共享"命名管道"的资源。
————Android中的IPC机制是为了让Activity和Service之间可以随时的进行交互,故在Android中该机制,只适用于Activity和Service之间的通信,类似于
远程方法调用,类似于C/S模式的访问。通过定义AIDL接口文件来定义IPC接口。Server端实现IPC接口,Client端调用IPC接口本地代理。
22.NDK是什么
A:
NDK是一些列工具的集合,NDK提供了一系列的工具,帮助开发者迅速的开发C/C++的动态库,并能自动将so和java 应用打成apk包。
NDK集成了交叉编译器,并提供了相应的mk文件和隔离cpu、平台等的差异,开发人员只需简单的修改mk文件就可以创建出so。
23.瀑布流是怎么实现的?
A:
————其实瀑布流的大体布局是由一个ScrollView和LinearLayout组成,LinearLayout包裹在ScrollView中,而其中的每一列也是一个LinearLayout布局,
内部为垂直分布,根据要求来显示分为n列,通过要求的列数来判断创建n个LinearLayout布局,而每个列的宽度是整个屏幕宽度的1/n,高度为铺满全屏,
通过将请求来的图片进行每组n个的依次循环往n列中各个填充。
24.侧滑是怎么实现的
A:
————首先还是讲一下实现原理。在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局。两个布局横向排列,
菜单布局在左,内容布局在右。初始化的时候将菜单布局向左偏移,以至于能够完全隐藏,这样内容布局就会完全显示在Activity中。然后通过监听手
指滑动事件,来改变菜单布局的左偏移距离,从而控制菜单布局的显示和隐藏。
25.你对缓存的理解,为什么要进行缓存
A:
————客户端缓存机制是android应用开发中非常重要的一项工作,使用缓存机制不仅仅可以为用户节省3G流量,同时在用户体验方面也是非常好的选择.
缓存机制分为两部分,一部分是文字缓存,另一部分是多媒体文件缓存.
缓存的优点
1.服务器的压力大大减小
2. 客户端的响应速度大大变快(用户体验)
3. 客户端的数据加载出错情况大大较少,大大提高了应有的稳定性(用户体验)
4. 一定程度上可以支持离线浏览(或者说为离线浏览提供了技术支持)
26.Sqlite怎么进行版本更新
A:
通过SQLiteOpenHelper类中的onUpgrade方法来判断比较版本号进行是否版本更新。
27.Sqlite怎么插入数据,插入一万条数据怎么操作
A:
在执行SQL语句前开启事务,在执行完后再关闭事务。
例如:
long starttime = System.currentTimeMillis();
System.out.println(starttime+"");
myDB.beginTransaction();
for (int i = 0; i< 2000;i++){
myDB.execSQL("insert into meetings (id ,mainid) values ( '1','1')");
}
myDB.setTransactionSuccessful();
myDB.endTransaction();
28.Activity的生命周期,点击home键时生命周期是什么样的?弹出一个窗口的时候生命周期是什么?
A:
————Activity生命周期:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestroy()->onReStart()
————Home键: onPause()->onStop()
————窗口: 一种是onPause()(未覆盖整个activity)
一种是onPause()->onStop()(覆盖了整个activity)
29.如何把activity设置成窗口模式
A:
在AndroidManifest.xml中相应的<activity>标签内设置属性android:theme=”@android:style/Theme.Dialog”
30.用过自定义的View吗?如何用
A:
RoundProgressBar
————自定义View其实就是继承View,并实现onDraw(),OnMeasure(),OnLayout()方法。
①如果需要改变View绘制的图像,那么需要重写OnDraw方法。(这也是最常用的重写方式。)
②如果需要改变view的大小,那么需要重写OnMeasure方法。
③如果需要改变View的(在父控件的)位置,那么需要重写OnLayout方法。
31.Arraylist和linkedlist有什么区别,它们都是线程安全的吗?
A:
ArrayList数组实现,轻量级(消耗系统资源少),运行快,线程不安全;
LinkedList 链表实现 常用于堆栈与队列的实现,不是线程安全的;
注: 数组元素连续顺序存放,查询快, 增删慢
链表元素顺序存放,查询慢, 增删快
32.用没用过事务,怎么用
A:
————sqlite 是支持事务处理的。
如果要同步删除很多数据,把它们做成一个统一的事务。通常一次 sqlite3_exec 就是一次事务,如果你要删除1万条数据,sqlite就做了1万次:
开始新事务->删除一条数据->提交事务->开始新事务->… 的过程。这个操作是很慢的。因为时间都花在了开始事务、提交事务上。
可以把这些同类操作做成一个事务,如下题所示。
33.Sqlite怎么插入数据,插入一万条数据怎么操作
A:
————在执行SQL语句前开启事务,在执行完后再关闭事务。
例如:
long starttime = System.currentTimeMillis();
System.out.println(starttime+"");
myDB.beginTransaction();
for (int i = 0; i< 2000;i++){
myDB.execSQL("insert into meetings (id ,mainid) values ( '1','1')");
}
myDB.setTransactionSuccessful();
myDB.endTransaction();
34.Android中的五种存储方式及其应用场景
A:
1)SharedPreferences
存储路径:(data/data/packagename/shares_prefs), 轻量级存储,以键值对的形式存储在xml中,一般用来保存应用中的设置属性
2)文件存储 SD卡存储多媒体文件, 文件缓存
3) Sqlite数据库 存储路径:(data/data/packagename/databases), 一种嵌入式数据库,支持sql语言,存储大量结构性数据
4)ContentProvider 进程(应用程序)间数据共享,数据源可以是sqlite,也可以是xml,相关类: ContentResolver(内容解析器), ContentObserver(数据 观察者)
5) 网络存储 天气数据的xml,json格式等等,通过HttpUrlConnection,HttpClient,或者SOAP协议获取数据
35.listview优化策略?
A:
1)、对convetView进行判空,是当convertView不为空的时候直接重新使用convertView。从而减少了很多不必要的View的创建
2)定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可
3)、当ListView加载数据量较大时可以采用分页加载和图片异步加载
36.ListView分页加载实现思路?
A:
————实现OnScrollListener 接口重写onScrollStateChanged 和onScroll方法,使用onscroll方法实现”滑动“后处理检查是否还有新的记录,如果有,
调用 addFooterView,添加记录到adapter, adapter调用 notifyDataSetChanged 更新数据;如果没有记录了,把自定义的mFooterView去掉。使用
onScrollStateChanged可以检测是否滚到最后一行且停止滚动然后执行加载
37.ListView图片异步加载实现思路?
A:
1.先从内存缓存中获取图片显示(内存缓冲)
2.获取不到的话从SD卡里获取(SD卡缓冲,,从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅)
3.都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示(视情况看是否要显示)
38.Intent的原理,作用,可以传递哪些类型的参数?
A:
————intent是连接Activity, Service, BroadcastReceiver, ContentProvider四大组件的信使,,可以传递八种基本数据类型以及string, Bundle类型,
以及实现了Serializable或者Parcelable的类型。
Intent可以划分成显式意图和隐式意图。
显式意图:调用Intent.setComponent()或Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给
哪个组件。
隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据类型)
找到最合适的组件来处理这个意图。
39.如何实现屏幕分辨率的自适应?
A:
————尽量使用Relativelayout
————通过权重(layout_weight)的方式来分配每个组件的大小比例,使用dip设备独立像素,不依赖像素
————drawable-hdpi,drawable-mdpi,drawable-ldpi放置不同分辨率图片
————通过.9.png实现图片的自适应
————已知应用支持平台设备的分辨率,可以提供多个layout_320*480
40.简述Android中的IPC机制
A:
————IPC(Inter-Process Communication,进程间通信),
————aidl是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口.
编译器可以通过扩展名为aidl的文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的.
————BroadcastReceiver也可以实现进程间通信
————ContentProvider 提供进程间数据共享
41.android哪几种方式访问网络?
A:
————HttpURLConnection
————HttpClient方式(HttpGet和HttpPost类)
HttpClient的通信过程
1.生成请求对象(HttpGet get,HttpPost post)
2.生成客户端对象 HttpClient client
3.执行请求接收相应 HttpResponse response = client.execute(post)
HttpEntity entity = response.getEntity()
4.得到数据流
InputStream inputStream = entity.getContent();
5.最后关闭过期连接
42.移动互联数据交互格式有哪些及其区别?(Json与xml的区别?)
A:
————移动互联数据交互格式有XML和JSON
1.JSON和XML的数据可读性基本相同
2.JSON和XML同样拥有丰富的解析手段
3.JSON相对于XML来讲,数据的体积小
4.JSON与JavaScript的交互更加方便
5.JSON对数据的描述性比XML较差
6.JSON的速度要远远快于XML
43.XML解析有哪几种?各自优缺点,官方推荐使用哪种?
A:
————基本的解析方式有三种: DOM,SAX,Pull
1.dom解析解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用 DOM 接口来操作这个树结构的优点是对文档增删改查比较方便,
缺点占用内存比较大。
2.sax解析基于事件驱动型,优点占用内存少,解析速度快,缺点是只适合做文档的读取,不适合做文档的增删改查。
3.pull解析同样基于事件驱动型,android 官方API提供,可随时终止
44.GC内存泄露在什么情况下回出现?怎么解决?
A:
————查询数据库没有关闭游标
————构造Adapter时,没有使用缓存的 convertView
————Bitmap对象不在使用时调用recycle()释放内存
————不用的对象没有及时释放对象的引用
45.android内存的优化
A:
android内存泄露容易导致内存溢出,又称为OOM。
Android内存优化策略:
1)在循环内尽量不要使用局部变量
2)不用的对象即时释放,即指向NULL
3)数据库的cursor即时关闭。
4)构造adapter时使用缓存contentview
5)调用registerReceiver()后在对应的生命周期方法中调用unregisterReceiver()
6)即时关闭InputStream/OutputStream。
7)android系统给图片分配的内存只有8M, 图片尽量使用软引用, 较大图片可通过BitmapFactory缩放后再使用,并及时recycle
8)尽量避免static成员变量引用资源耗费过多的实例。
46.加载大图片的时候如何防止内存溢出
A:
android系统给图片分配的内存只有8M,当加载大量图片时往往会出现OOM。
Android加载大量图片内存溢出解决方案:
1)尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都
是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView
的 source
2)使用BitmapFactory.Options对图片进行压缩
InputStream is = this.getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 10; //width,hight设为原来的十分一
Bitmap btp =BitmapFactory.decodeStream(is,null,options);
3)运用Java软引用,进行图片缓存,将需要经常加载的图片放进缓存里,避免反复加载及时销毁不再使用的Bitmap对象
if(!bmp.isRecycle() ){
bmp.recycle() //回收图片所占的内存
system.gc() //提醒系统及时回收
}
————内存溢出是因为程序运行所需要的内存大于系统能分配给你的内存。看那些内存可以优化的就优化,比如图片过多就用软引用,弱引用。
虽然java不能自己释放内存。但你用过的对象,及时消除引用。再调用一下gc我觉得有时候还蛮好使的
47.Android缓存机制
A:
客户端缓存机制是android应用开发中非常重要的一项工作,使用缓存机制不仅仅可以为用户节省3G流量,同时在用户体验方面也是非常好的选择,比
如有些新闻客户端支持离线模式,也是通过缓存机制实现的.缓存机制分为两部分,一部分是文字缓存,另一部分是多媒体文件缓存.
文字缓存有两种实现:
1)可以将与服务器交互得到的json数据或者xml数据存入sd卡中,并在数据库添加该数据的记录.添加数据库记录时,提供两个关键字段,一个是请求
的URL,另一个则是本地保存后的文件地址,每次加载数据之前都会根据URL在数据库中检索
2)将JSON数据解析后装入List<Map>对象中,然后遍历List,将数据统统写入相应的数据库表结构中,以后每次向服务器发起请求之前可以先在数据库
中检索,如果有直接返回.
多媒体文件缓存:主要指图片缓存
图片的缓存可以根据当前日期,时间为名字缓存到SD卡中的指定图片缓存目录,同时数据库中做相应记录,记录办法可以采用两个关键字段控制,一个字
段是该图片的URL地址,另一个字段是该图片的本机地址.取图片时根据URL在数据中检索,如果没有则连接服务器下载,下载之后再服务器中作出相应记录
缓存文件删除策略:
1. 每一个模块在每次客户端自动或者用户手动更新的时候删除相应模块的缓存文件,并重新下载新的缓存文件.
2. 在设置界面中提供删除缓存的功能,点击后删除本机所有缓存.
48.如何实现消息推送,有哪些方式,各自优缺点,最常使用哪种?
A:
实现消息推送的方式有五种,分别是轮询,SMS,C2DM,MQTT,XMPP最常使用的是XMPP, 我们做项目时采用的是XMPP协议
1.XMPP协议,它是一种基于XML的传递协议,具有很强的灵活性和可扩展性。它的特点是将复杂性从客户端转移到了服务器端。GTalk、QQ、IM等都
用这个协议。
2.轮询:客户端定时去服务端取或者保持一个长Socket,从本质讲这个不叫推送,而是去服务端拽数据。但是实现简单,主要缺点:耗电,浪费用户
流量等
3.Google的C2DM,具体不细说,缺点,服务器在国外,不是很稳定。
4.通过短信方式, 但是很难找到免费短信平台
5. MQTT协议, IBM提供的一种推送服务,不太灵活
49.MVC在Android中的应用
A:
Android中界面部分也采用了当前比较流行的MVC框架,在Android中:
1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。也可以使用JavaScript+HTML等的方式作为View层,通过
WebView组件加载,同时可以实现Java和JavaScript之间的通信。
2) 控制层(Controller):这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是
Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,Android的控制层的重任通常落在了众多的Acitvity的肩上,程序就很容易被回收掉。
3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。
在Android SDK中的数据绑定,也都是采用了与MVC框架类似的方法来显示数据。在控制层上将数据按照视图模型的要求(也就是Android SDK中的Adapter)
封装就可以直接在视图模型上显示了,从而实现了数据绑定。比如显示Cursor中所有数据的ListActivity,其视图层就是一个ListView,将数据封装为
ListAdapter,并传递给ListView,数据就在ListView中显示。
50.Android自定义组件实现思路
A:
Android自定义组件有三种实现思路:
————继承某个现有组件,在其基础上添加额外功能,如继承Gallery实现CoverFlow效果
————继承某个Layout,实现复合组件自定义,如TextView和EditText组合实现登录注册组件
————继承View,实现onDraw()方法,实现自己绘制组件,如翻页效果组件
51.版本更新的实现思路
A:
————在服务器相应URL上有版本文件, 客户端同时存储该应用当前版本号 (SharedPreferences/Sqlite), 每次打开应用,去检测服务器版本号与本地版本号是
否一致,如果不一 致,则自定义对话框提示是否下载更新
52.播放视频有哪些实现方式?
A:
1.使用系统自带的播放器来播放,指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
//调用系统自带的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
2. 使用VideoView组件来播放, 可以结合MediaController来实现播控, 只是不能随意更改视频的大小及位置。
3. 使用MediaPlayer和SurfaceView来实现,这种方式很灵活,可以自定义视频播放的大小和位置。
53.谈谈UI中, Padding和Margin有什么区别,gravity与layout_gravity的区别
A:
Padding 用来指定组件内的内容距离组件边界的距离;
Margin用来指定控件与控件之间的距离
Gravity用来指定组件内的内容相对于组件本身的位置
Layout_gravity用来指定组件相对于其父组件的位置
54.哪个组件可以实现手风琴效果,用来实现设置界面的类,实现抽屉效果, 悬浮窗口?
A:
————实现手风琴效果(ExpandableListView)
————设置界面的类(preferenceActivity)保存到sharedpreference中
————抽屉效果(slidingDrawer)组件
————悬浮窗口: PopWindow,可以实现类似Dialog和菜单的效果
55.常用设计模式及应用场景,用两种方式实现单例模式,要求线程安全?
A:
————常用设计模式:
单例模式:
适配器模式: Adapter 为ListView GridView等添加数据
工厂模式:
代理模式: AIDL
观察者模式: 服务订阅
门面模式:对外统一接口
策略模式:扫描策略
模板模式:提取共用算法, 模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
56.android客户端如何实现自动登录
A:
通过SharedPreferences存储用户名,密码,当存储不为空时实现自动登录功能
57.在Android中,进行http编程有几种方式?
A:
在Android中,可以有两种方式可以用来进行Http编程:1、HttpURLConnection;2、HttpClient
————HttpURLConnection是继承自URLConnection的一个抽象类,在HTTP编程时,来自HttpURLConnection的类是所有操作的基础;
HttpURLConnection对网络资源的请求在默认情况下是使用GET方式的,所以当使用GET方式时,不需要我们做太多的工作:
Http中的get请求,在Url中带有请求的参数,请求的URL格式通常为:"http://XXX.XXXX.com/xx.aspx?param=value"
当我们需要使用POST方式时,就需要使用setRequestMethod()来设置请求方式了。Http中的post请求,不在Url中附加任何参数,
这些参数都会通过cookie或者session等其他方式以键值对的形式key=value传送到服务器上,完成一次请求。请求的URL格式通常为:
"http://XXX.XXXX.com/xx.aspx"
————HttpClient:在Android SDK中提供了Apache HttpClient(org.apache.http.*)模块。但是这个类并不是来自Android的,而是来
自org.apache.http。与HttpURLConnection相同,HttpClient也存在GET和POST两种方式。
HttpGet 在HttpClient中,可以非常轻松使用HttpGet对象来通过GET方式进行数据请求操作,当获得HttpGet对象后我们可以使用
HttpClient的execute方法来向我们的服务器发送请求。在发送的GET请求被服务器相应后,会返回一个HttpResponse响应对象,利用
这个响应的对象我们能够获得响应回来的状态码,如:200、400、401等等
HttpPost 使用POST方式时,我们可以使用HttpPost类来进行操作。当获取了HttpPost对象后,我们就需要向这个请求体传入键值对,
这个键值对我们可以使用NameValuePair对象来进行构造,然后再使用HttpRequest对象最终构造我们的请求体,最后使用HttpClient的
execute方法来发送我们的请求,并在得到响应后返回一个HttpResponse对象。其他操作和我们在HttpGet对象中的操作一样
————HttpGet与HttpPost的异同
共同点,HttpGet和HttpPost创建方式相同:
A.创建HttpGet(或HttpPost)对象,将要请求的URL通过构造方法传入HttpGet(或HttpPost)对象中;
B.使用DefaultHttpClient类的execute方法发送HTTP GET或HTTP POST 请求,并返回HttpResponse对象;
C.通过HttpResponse接口的getEntity方法返回响应信息。
不同点,HttpPost在使用是需要传递参数 ,使用List<NameValuePair>添加参数。
————HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没
有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收响应,以及管理HTTP连接。
57.Android OOM 产生的几种原因?
A:
1. 程序中使用了太多自己创建的Bitmap.
大部分情况是因为重复创建bitmap, 而不使用的bitmap没有被及时释放, 导致了 oom. 所以在不使用的时候要将bitmap对象回收bitmap.recycle(),
并将bitmap对象置为null.
还有就是当你一次性使用过多bitmap的时候也会导致oom. 比如使用系统的Gallery组件, 然后在每一项使用自己的图片, 这个时候我们
通常自定义Gallery的adapter. 当Gallery需要显示很多图片的时候, 而我们没有做图片缓存机制的话必然会导致oom发生. 所以这个时候需
要做图片缓存机制. 例如只保存当前显示项前后3项的图片, 滑动Gallery的时候再根据当前项动态回收前后不需要的图片和加载需要的图片.
2. 程序中一些对象创建的时候需要context的时候.
这种情况, 通常我们会使用create(this);这个时候引用的时候activity的context, 如果该对象没有被及时回收, activity的引用将被它保留,
从而导致activity不能被及时销毁, 当重复创建activity后, 就会导致activity有多个实例,从而导致内存泄露.所以在用到context的时候尽量使用
application的context: getApplicationContext().
3. 查询数据库没有关闭游标
程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor 后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被
发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。所以在使用完游标以后要cursor.close();
4.构造Adapter时,没有使用缓存的convertView
以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法:
publicView getView(int position, View convertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会
从BaseAdapter中根据当前的屏幕布局实例化一定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上
面的list item的view对象会被回收,然后被用来构造新出现的最下面的listitem。这个构造过程就是由getView()方法完成的,getView()的第二个形
参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使
用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。
Android进阶之图片缩略图(解决大图片溢出问题)
大图片的溢出问题,通过缩略图的方式避免。主要是通过BitmapFactory.Options 来实现。
第一步:BitmapFactory.Option设置 inJustDecodeBounds为true
第二步:BitmapFactory.decodeFile(path,option)方法解码图片路径为一个位图。如果指定的文件名是空的,或者不能解码到一个位图,函数将返回null[空值]。
获取到outHeight(图片原始高度)和 outWidth(图片的原始宽度)
第三步:计算缩放比例,也可以不计算,直接给它设定一个值。options.inSampleSize = "你的缩放倍数";如果是2就是高度和宽度都是原始的一半。
第四步:设置options.inJustDecodeBounds = false;重新读出图片 bitmap = BitmapFactory.decodeFile(path, options);
1.缩略图
2.软引用
3.及时回收
大部分的电都消耗在了网络连接、GPS、传感器上了,简单的说也就是主要在以下情况下耗电比较多:
1、 大数据量的传输。
2、 不停的在网络间切换。
3、 解析大量的文本数据。
改善程序
1、在需要网络连接的程序中,首先检查网络连接是否正常,如果没有网络连接,那么就不需要执行相应的程序。
2.使用效率高的数据格式和解析方法。
通过测试发现,目前主流的数据格式,使用树形解析(如DOM)和流的方式解析(SAX)对比情况如下图所示,很明显,使用流的方式解析效率要高一些,
因为DOM解析是在对整个文档读取完后,再根据节点层次等再组织起来。而流的方式是边读取数据边解析,数据读取完后,解析也就完毕了。
在数据格式方面,JSON和Protobuf效率明显比XML好很多,XML和JSON大家都很熟悉,Protobuf是Google提出的,一种语言无关、平台无关、扩展性好的
用于通信协议、数据存储的结构化数据串行化方法。
从上面的图中我们可以得出结论就是尽量使用SAX等边读取边解析的方式来解析数据,针对移动设备,最好能使用JSON之类的轻量级数据格式为佳。
3、使用GZIP压缩方式下载数据,能减少网络流量;目前大部门网站都支持GZIP压缩,所以在进行大数据量下载时,尽量使用GZIP方式下载
HttpEntity entity = response.getEntity();
InputStream compressed = entity.getContent();
InputStream rawData = new GZIPInputStream(compressed);
4.其它一些优化方法:
回收java对象,特别是较大的java对像
对定位要求不是太高的话尽量不要使用GPS定位,可能使用wifi和移动网络cell定位即可。GPS定位消耗的电量远远高于移动网络定位。
尽量不要使用浮点运算
很多人开发的程序后台都会一个service不停的去服务器上更新数据,在不更新数据的时候就让它sleep,这种方式是非常耗电的,通常情况下,我们
可以使用AlarmManager来定时启动服务
最后一招,在运行你的程序前先检查电量,电量太低,那么就提示用户充电之类的
在android开发中使用http协议比较简单,sdk已经做了很好的封装了,具体使用方法可以参考我的这篇博文。而在游戏开发中,可以结合使用http和socket,
当然了http协议底层也是基于tcp协议的。http协议是无连接、无状态的,每次连接只能处理一个请求,然后就断了,而且发一个请求需要附加额外信息(请求行、
请求头),每次请求都需要重新建立连接;使用socket的好处是更高效和省流量,建立一次连接后,只要不手动或者出现异常断开,就可以一直互相发送数据,而
且是直接以字节的形式发送,不需要额外的附加信息,缺点就是难度加大了,需要服务端和客户端很好的配合,保证发送和读取时数据的顺序一致
57.IntentService?
IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中
的onHandleIntent(Intent)方法接收一个Intent对象,在适当的时候会停止自己(一般在工作完成的时候). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求
notify和notifyAll的区别?
线程的同步?
线程的同步是保证多线程安全访问竞争资源的一种手段
在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
当然这不是唯一控制并发安全的途径。
IOS与Android之间有什么区别?
Java中是如何体现面向对象思想的?
Java中是如何体现面向对象思想的?
Android中你是怎么实现多线程的?
SQLite的事务处理,添加删操作怎么进行优化
xml和json都做过,我还直接把自己曾经做的项目涉及到数据交互
你知道如何实现断点续传么,请你简单说一下
熟悉xmpp么
假如下载文件的时候,下载到一半突然间断了,怎么把资源回收?
熟悉java当中比较复杂的API么?具体调用了什么类和方法?比如说反射机制
Android连连看,你用到的是什么框架
8个球,其中一个球与其他7个球不同,给你一个天平,你如何在最短的时间里找到这个球
排序算法有哪些?你来演示一下快速排序的实现过程(画图),如果利用两个栈实现一个队列
String跟StringBuffer有什么区别,String是否能被继承,C跟Java有什么区别,Java回收机制,它是怎么实现的?怎么判断一个对象是不可达的?
侧滑
推送
常用的设计模式的应用场景有一定了解
项目经验
比如Android系统的性能管理,如何控制GC
网络知识不可少,http协议、tcp/ip协议、socket可能都需要去深入了解一下