1.线程
Java新建线程的方式:1)实现Runnable接口,2)继承Thread类。
2.多态
Java 中的引用变量有两个类型,一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定。如果编译时的类型与运行时的类型不一致就会出现所谓的多态。(Polymorphism)
例题如下:
现实生活中也有多态的原型:例如一个父亲F有两个孩子S1和S2,而父亲又可以代表孩子做一些事情,即F即可以代表S1也可以代表 S2,因此F具有一定的多态性。在Java中多态大多是指对象变量的多态,即一个F类型的变量既可以指向F类型的对象也可以指向S1、S2类型的对象。(F与S1、S2之间需要存在继承关系)
注意:除了上述多态形式外,一个接口类型变量也可以指向其实现类的实例,这也是多态的一种表现。
(参考:《你必须知道的261个Java问题》)
哈哈,上面的这幅图是不是很形象?吃糖相当于方法,而年龄则相当与属性。
关于强制类型转换
v 基本类型之间的强制转换只能在数值之间进行,这里所说的数值类型包括整数型、字符型和浮点型。但数值型不能和布尔型之间进行转换。
v 引用类型之间的转换只能把一个父类变量转换成子类类型,如果两个没有继承关系的类型,则无法进行类型转换,否则编译时会出现错误。如果试图把一个父类实例转换子类类型,则必须这个对象实际上是子类才行(即编译时类型为父类类型,而运行时类型是子类类型),否则在运行时会发生 ClassCastException异常。
为了避免出现ClassCastException异常,可以使用instance of运算符判断是否可以转型成功。前面的程序可以加上:
If(objStr instanceof String){
String str=(String) objPri;
}
注意点:
当把子类对象赋给父类时,被称为向上转型(upcasting),这种转型总是可以完成的,这也从一个侧面证明了子类是一种特殊的父类。这种转型只是表明这个引用变量的编译类型是父类,但实际执行他的方法时,依然表现出子类对象的行为方式。但把一个父类对象赋给子类引用变量时,就需要进行强制类型转换。
instanceof 运算符的前一个数通常是一个引用类型的变量,后一个操作数通常是一个类(也可以是接口,可以把接口理解成一种特殊的类),它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是则返回true,否则返回false。
使用instanceof 运算符事应该注意,前面的操作数的编译类型要么与后面的类相同,要么是后面类的父类,否则会引起编译错误。
java的编译时多态和运行时多态
1、运行时多态和编译时多态的区别?
编译时的多态,是指参数列表的不同, 来区分不同的函数, 在编译后, 就自动变成两个不同的函数名. 在运行时谈不上多态
运行时多态:用到的是后期绑定的技术, 在程序运行前不知道,会调用那个方法, 而到运行时, 通过运算程序,动态的算出被调用的地址. 动态调用在继承的时候,方法名 参数列表完全相同时才出现运行时多态!
运行时多态,也就是动态绑定,是指在执行期间(而非编译期间)判断所引用对象的实际类型,根据实际类型判断并调用相应的属性和方法.看看下面这个例子:
abstract class Animal {
private String name;
Animal(String name) {this.name = name;}
/*
public void enjoy(){
System.out.println("叫声......");//父类方法,下面的子类对该方法不满意,重写.
}
*/
public abstract void enjoy();//该方法没有实现的必要(所以我们一般声明为静态方法),但有定义的必要.
}
class Cat extends Animal {
private String eyesColor;
Cat(String n,String c) {super(n); eyesColor = c;}
public void enjoy() {
System.out.println("猫叫声......");
}
//public abstract void enjoy();//如果不想实现父类静态方法,可将此方法声明为静态的,该类也同时为abstract
}
class Dog extends Animal {
private String furColor;
Dog(String n,String c) {super(n); furColor = c;}
public void enjoy() {
System.out.println("狗叫声......");
}
}
class Bird extends Animal {
Bird() {
super("bird");
}
public void enjoy() {
System.out.println("鸟叫声......");
}
}
class Lady {
private String name;
private Animal pet;
Lady(String name,Animal pet) {
this.name = name; this.pet = pet;
}
public void myPetEnjoy(){pet.enjoy();}
}
public class Test {
public static void main(String args[]){
Cat c = new Cat("catname","blue");
Dog d = new Dog("dogname","black");
Bird b = new Bird();
//Lady l1 = new Lady("l1",c);
Lady l2 = new Lady("l2",d);
Lady l3 = new Lady("l3",b);
//l1.myPetEnjoy();
l2.myPetEnjoy();
l3.myPetEnjoy();
}
}
动态绑定的底层实现是指针,我们知道程序中的方法是一段代码,存放在专门的内存区域中(code segment---代码区),当我们在程序执行期间new 出一个对象后调用enjoy方法的时候,JVM动态的把指针指向实际的对象重写的方法,从而实现了动态绑定.
动态绑定的最大好处就是给我们的程序的可扩展性带来了相当大的提高(上面的例子中我们可以继续添加子类或是在main方法中new lady,),如果没有动态绑定,我们一般情况下的做法是在子类中用instanceof判断一个对象是不是我们需要的当前具体对象,但当我们定义好多个子类的时候,每次都要判断,现在有了动态绑定,我们不需要再去判断,而是JVM动态给我们指向具体的对象并调用相应的属性和方法.
3.service生命周期
startService与bindService的区别
Service的生命周期方法比Activity少一些,只有onCreate, onStart, onDestroy
我们有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。
1 通过startService
Service会经历 onCreate --> onStart
stopService的时候直接onDestroy
如果是 调用者 直接退出而没有调用stopService的话,Service会一直在后台运行。
下次调用者再起来仍然可以stopService。
2 通过bindService
Service只会运行onCreate, 这个时候 调用者和Service绑定在一起
调用者退出了,Srevice就会调用onUnbind-->onDestroyed
所谓绑定在一起就共存亡了。
注意:Service的onCreate的方法只会被调用一次,
就是你无论多少次的startService又 bindService,Service只被创建一次。
如果先是bind了,那么start的时候就直接运行Service的onStart方法,
如果先是start,那么bind的时候就直接运行onBind方法。如果你先bind上了,就stop不掉了,
只能先UnbindService, 再StopService,所以是先start还是先bind行为是有区别的。
4.sax/dom解析
SAX:只能读,不能修改,只能顺序访问,适合对大型的XML的解析,解析速度快!
DOM:不仅能读,还能修改,而且能够实现随机访问,缺点是解析速度慢,只适合解析小型文档
解析速度慢(要在内存中生成节点树,而生成树是比较费时的)
SAX:应用于保存大量数据的XML(为什么要用XML保存大量的数据类容?答:可以实现异构系统
的数据访问,实现跨平台!)
DOM:一般应用与小型的配置XML,方便我们操作!
SAX是Simple API for XML的缩写,它并不是由W3C官方所提出的标准,可以说是“民间”的事实标准。实际上,它是一种社区性质的讨论产物。虽然如此,在XML中对SAX的应用丝毫不比DOM少,几乎所有的XML解析器都会支持它。
与DOM 比较而言,SAX是一种轻量型的方法。我们知道,在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个 Node对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力。特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用DOM是一件很不划算的事(比如在applet中)。这时候,一个较好的替代解决方法就是SAX。
SAX 在概念上与DOM完全不同。首先,不同于DOM的文档驱动,它是事件驱动的,也就是说,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。(如果你对Java新的代理事件模型比较清楚的话,就会很容易理解这种机制了)
在XMLReader接受XML文档,在读入XML文档的过程中就进行解析,也就是说读入文档的过程和解析的过程是同时进行的,这和DOM区别很大。解析开始之前,需要向XMLReader注册一个ContentHandler,也就是相当于一个事件监听器,在 ContentHandler中定义了很多方法,比如startDocument(),它定制了当在解析过程中,遇到文档开始时应该处理的事情。当 XMLReader读到合适的内容,就会抛出相应的事件,并把这个事件的处理权代理给ContentHandler,调用其相应的方法进行响应。
5.broadcast两种注册方式
第一种在代码中使用registerReceiver注册
//注册自定义BroadcastReceiver(1)
this.registerReceiver(mBroadcastReceiver, new IntentFilter("com.xxx.action.MYRECEIVER"));
Intent intent = new Intent("com.xxx.action.MYRECEIVER");
this.sendBroadcast(intent);
在代码中注册,这种方式可以完成类似这样的场景,满足某条件时注册广播接收者来接收广播,在另一种条件下时便解除注册广播接收者不再接收广播。具体就是通过两个方法来实现注册和解除注册
第二种在清单文件androidmanifest中注册(常驻Receiver)
<receiver android:name=".InfoReceiver">
<intent-filter>
<action android:name="com.mobimtech.action.BROADCAST"/>
</intent-filter>
</receiver>
但是使用这种注册方式注册了之后,即使程序已经关闭或者没有开启,都将接收到匹配的广播。
�l送�V播的三�N方法:
Context.sendBroadcast ��多���V播接收器的�r候,接收�V播的�序�]有保�C
Context.sendOrderedBroadcast ��多���V播接收器的�r候,接收�V播的�序按�]��rIntenFilter�O置的��先���绦小�
Context.sendStickyBroadcast ��多���V播接收器的�r候,接收�V播的�序�]有保�C;Intent在�l送后��一直存在,以后�]�缘�V播接收器也��收到。
6.activity生命周期
A B | A B
oncreate | oncreate
onstart | onstart
onResume | onResume
按键跳转至B | 按键跳转至B
onpause | onpause
oncreate | oncreate
onstart | onstart
onresume | onresume
onstop | onstop
============================= | =====================================
按键跳转至A | 按back键返回
onpause | onpause
oncreate | oncreate
onstart | onstart
onresume | onresume
onstop | onstop
| ondestory
7.GIT
1.基本命令。
/**
*这里列举几个常见的git命令,让大家过过目
**/
1)基本操作
>git init //初始化版本库
>git clone //克隆版本库
>git add //添加新文件
>git commit //提交到本地版本库
>git checkout //检出(分支、标签)
2)分支
>git branch //列出分支
>git branch -r //列出远程分支
>git branch -a //列出所有分支
>git branch newBranch //基于当前分支创建新分支newBranch
>git branch -D branchName //删除分支branchName
>git branch -d branchName //仅删除已合并分支branchName
>git merge <--> //合并分支<br>>git tag
3)历史
>git log //显示全部历史
>git log -p //显示版本历史,以及版本间的内容差异
>git log -5 //显示最近的5个提交
>git log -5 -p //显示最近的5个提交,以及版本间的内容差异
>...(很多很多参数...)
>git diff 112 115 //显示112和115版本的差别
4.添加文件
git中存放代码的地方有三个,第一个是工作目录树,也就是我们看到的代码文件夹;第二是暂存区,是本地中和远程版本库的一个缓冲区域,暂存区一般存放的是本地准备要提交到远程版本库的修改;第三个是远程版本库。
git add命令是把工作目录树中的修改添加到暂存区。
> cd qianxudetianxia/
> touch test.txt //创建test.txt文件
> git add test.txt //添加到本地版本库
> touch a.txt b.txt //创建多个文件
> git add . //提交当前文件夹的增加项到本地版本库
5.提交到本地版本库
//git commit是提交命令,把修改提交到本地版本库中
//-m参数,添加说明文字
> git commit -m 'add the three new file'
[master a0885c9] add the three new file
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a.txt
create mode 100644 b.txt
create mode 100644 test.txt
提交成功。
6.查看分支。
>git branch -a
//输出如下,其中master是本地分支,origin是远程版本库的别名
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
7.合并到远程版本库。
//把本地分支master的修改合并到远程版本库origin中(的远程分支master)
> git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 294 bytes, done.
Total 2 (delta 0), reused 0 (delta 0)
To [email protected]:fjtianxia/qianxudetianxia.git
8a7d54d..a0885c9 master -> master
到此,git的最基本使用已经熟悉了。
8.查看log。
git log命令用于查看修改日志,它的参数非常丰富,在此不一一列举了,参考本文开始的基本命令部分。
9.查看分支 。
我们先查看分支,再添加新的分支branch1来看看效果:
新建分支后,我们可以通过checkout切换到指定分支。
绿色颜色标识了当前所在分支。
除了可以新建分支,也可以删除分支:
分支功能是git最强大的一部分,还可以从基于远程分支创建新分支,合并分支等等。
10.查看标签。
Tag标签,是为了记录一些重要的事件,被打成一个标志,比如android的版本号等等里程碑事件。
tag和branch的添加,切换的使用方式基本相同。
tag是只读的,该tag对应的工作目录树内容不能再修改了,而branch则不是。
11.小结。
git真是一个很不错的东西,推荐大家去了解下,并在工作中能去普及使用。
8,Android.mk
(1)生成so库
Android.mk内的变量定义
3.1 LOCAL_PATH
一个Android.mk file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。例如:
1. LOCAL_PATH:= $(call my-dir)
宏函数’my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)
3.2 include $( CLEAR_VARS)
宏CLEAR_VARS 由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
3.3 LOCAL_SRC_FILES
本次需要编译的源文件
3.4 LOCAL_SHARED_LIBRARIES
本次编译需要链接的动态链接库文件,即.so文件
3.5 LOCAL_STATIC_LIBRARIES
静态链接库.
3.6 LOCAL_C_INCLUDES
本次编译需要包含的头文件,一个相对于当前目录可选的路径名单,当编译所有的源文件(C,C++和汇编)时,它将被添加进include搜索路径。例如:
LOCAL_C_INCLUDES := sources/foo
或者甚至:
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
3.6 LOCAL_LDLIBS
本次编译的链接选项,相当于gcc -l后的参数
3.7 LOCAL_CFLAGS
同样是编译选项,相当于gcc -O后面的参数
3.8 LOCAL_MODULE
生成的模块名,这个变量必须定义,表示make后将要生成的文件的名字
3.9 LOCAL_PACKAGE_NAME
apk文件名
4 include
include可Android多以这样的形式出现,如:include $( CLEAR_VARS),include $(BUILD_SHARED_LIBRARY).其实这个include可以理解成"执行"的意思,那么执行什么呢?当然是看后边的宏了.
宏CLEAR_VARS已经在3.2节中介绍过了,表示清除一些变量.
宏BUILD_SHARED_LIBRARY表示生成共享库,即生成.so文件
因此include $(BUILD_SHARED_LIBRARY)就是指定在/system/lib/目录下生成一个lib$(LOCAL_MOUDULE).so文件,同样类型的宏如下:
* CLEAR_VARS 清除LOCAL_xxx变量
* BUILD_SHARED_LIBRARY 在/system/lib/目录下生成lib$(LOCAL_MOUDULE).so文件
BUILD_STATIC_LIBRARY 生成lib$(LOCAL_MOUDULE).a文件
* BUILD_EXECUTABLE 在/system/bin/目录下生成可执行文件
BUILD_PACKAGE 编译成一个apk文件
(2)
生成apk。
10 抽象类和接口的区别
抽象类里面可以有非抽象方法
但接口里只能有抽象方法
声明方法的存在而不去实现它的类被叫做抽像类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的所有抽像方法提供实现,否则它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
接口(interface)是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对像上调用接口的方法。由于有抽像类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
抽象类的成员可以具有访问级别,而接口的成员全部public级别
抽象类可以包含字段,而接口不可以,
抽象类可以继承接口,而接口不能继承抽象类
抽象类的成员可以具有具体实现,而接口不行
抽象的子类可以选择性实现其基类的抽象方法,而接口的子类必须实现
abstract
修饰类:不能用new 实列化一个对象
但可以声明成一个抽象类的变量
修饰方法: 没有方法体,以分号结束
子类如果是抽象类,可以不实现;如果子类不是抽象类,一定要实现抽象方法
如果一个类里有一个抽象方法,此类必为抽象类
如果一个类是抽象类,里面的方法可以不为抽象方法
Interface
用interface来定义一个接口
里面的方法都没有方法体
所有的方法和属性的修饰符只能是 public或不写(默认public)
接口里能定义属性
用implements来实现一个接口
一个类可以实现多个接口
实现类“继承”接口的属性
实现类里要实现接口中的所有方法
接口不能用new 来实例化
但可以声明一个接口变量
1:抽象类一定有abstract修饰
2:抽象类里至少有一个由abstract修饰的方法(即抽象方法,无实现的)
3:接口一定有interface修饰
4:接口里的方法都为没实现的方法
5:接口里的方法可以是抽象的,也可以不是抽象的(可以无抽象方法,也可以全是抽象方法)