Java和OC之间的比较

出处:http://blog.sina.com.cn/s/blog_93742d0d010165qi.html


1.          Cocoa是什么?Cocoa是使用OC语言编写的工具包。里面有大量的类库和结构体,说白了其实就相当于java中的标准API或C++的标准库。OC中没有命名空间的概念,所以使用加前缀来防止命名冲突,因此你会看到大量的以NS为前缀的类名、结构体、枚举等。


2.          Cocoa框架由Foundation kit, App kit两部分组成,前者是基础工具库,后者主要是UI库和高级对象等。


3.          static标识的类变量定义在接口的外面,类变量只能本类访问,除非提供类方法给外部。


4.          @语法是OC特有的语法,C是没有的。


5.          OC中只有类的成员变量才有访问权限控制,@public, @protected, @private,默认是@protected。类变量、类方法、成员方法是没有访问修饰符的,所有的方法都是public的,所有的类变量都是私有的。


6.          OC中的类方法只能类调用,如果使用对象调用就会报错,而java中只是一个警告而已。


7.          OC中定义类的@interface和java中的interface不是一回事,OC的@protocol和java中的interface才是一码事。@interface跟java和C++ class关键字差不多,在OC和C++中,都习惯将定义写在头文件里,而java却根本没有头文件的概念,至于为什么有这样的差别,我想是为了OC和C++编译器方便,大多数时候编译器只需要知道头文件类的定义就知道这个类有哪些属性和方法了。


8.          get为前缀的方法在OC中有特殊的意义,所以尽量使用成员变量名称来作为getter方法的名称。


9.          OC是动态语言,所以即便是@interface中没有定义的方法在.m文件中实现了也是OK的,一般我们都是给别人提供头文件,这种情况就相当于把方法变相私有化了,对于我这种喜欢在写代码过程中随时加方法但又不想动头文件的人来说还是比较爽的。


10.        在OC中所有对象的存取都是用指针,C++中还有引用,OC中没有,我宁愿你强迫我使用一种方法,也不远你提供给我两种方法我却不知道使用哪一种方法的好。


11.        OC的方法定义和调用算是一大“特色”:

方法的定义为:

-(返回类型)方法名:(参数1类型)参数1变量 参数2标签:(参数2类型)参数2变量…
[类或者实例的指针方法名:参数1 标签2:参数2 … …]

为了好看,第一个参数一般不加标签名,当然,标签名都可以隐藏,但不建议这样做,因为当你接手了一个离职的人的程序,其中的java程序调用了一个有五个甚至更多的参数的方法,但是你手上没有这个方法的API,那么你很难猜得出来这五个参数到底都干什么用的,但是Objective-C调用的时候,每个参数前面都必须有方法的标签名,这样你便能很容易地从标签名看出这个参数是什么意思。


12.        调用类方法:

[Fraction t];
[[Fraction class] t];
Class clazz = [Fraction class];
[clazz t];


class来自于NSObject,相当于java中的getClass()方法,也就是获取这个类的Class对象,clazz前面没有*,这是因为Class已经是一个指针。另外这种嵌套调用的方式,你也要习惯,这就和java中的A.b().c()没有什么区别。

获取Class有如下几种方法:

[类或者对象 class]
[类或者对象 superclass]
NSClassFromString(类名的字符串形式)

你也可以通过如下的函数把Class转换为字符串形式:

NSStringFromClass(Class)

13.        OC中自定义类最好显式继承NSObject,可以获得alloc,init,release等方法。


14.        OC中使用nil表示null,但跟java中的null还是有区别的,java中调用null的方法,会报臭名昭著的空指针异常,而OC不会,所以在OC你不必再为空指针而烦恼。


15.        Cocoa中提供的很多函数和java中的api有很大的不一样,java是纯面向对象的,所有的方法都必须在类中定义,但OC是兼容C语言的,所以C中的面向过程思想在CO中也是行得通的,因此Cocoa中很多东西都不是对象,而是C语言的函数、结构体,比如NSLog()。


16.        对象的初始化

前面我们看到实例化对象最多的方法是:

Fraction *frac = [[Fraction alloc]init];

这跟java不一样,java对象创建只需要new一下,同时调用构造方法(实际上虚拟机层面分为两个步骤:new对象,执行<init>方法(包含构造方法)),但是OC中分为两步:分配内存(同事给变量赋初值)、初始化。

1.     alloc是从NSObject继承而来的类方法,用于给对象分配存储空间,所有的成员变量在此时都确定了自己的内存位置,并被赋初值,整数类型为0,浮点数为0.0,BOOL为no,alloc方法返回对象的指针。

2.     init这个方法是从NSObject继承而来的,你可以去覆盖它,当然init不是构造方法,只是一个不同方法而已,你甚至可以换成其他方法,只不过我们习惯在方法名前缀加上init。

还记得java中的构造方法没有返回值吧,所以为了达到java这种效果:Test t = new Test(2) -- OC中需要你手动在普通方法(相当于构造方法)中return self。


17.        NSObject中的description方法相当于java中的Object的toString方法,用于获取对象的字符串表示。唯一不同的是OC中需要格式化字符串%@,才会触发description被调用:

Fraction *frac = [[Fraction alloc]init];
NSLog(@”%@”,frac);

18.        OC的异常都继承自NSException,当然NSException本身也继承自NSObject。使用下面的语句块进行异常捕获:

@try{
   <#statements#>
}
@catch (NSException *exception){
   <#handler#>
}
@finally{
<#statement#>
}

总体来说,OC的异常体系和java中的异常体系差不多,唯一的差别就是java中有业务异常,意思就是说你必须不活他,如果不捕获就会出现编译错误,OC中的异常基本上都属于java中的运行时异常,如果你没有捕获,发生了异常程序就会崩溃,在一些循环批处理任务中尤其要注意。

 

19.        OC中还有一个特色那就是他的弱类型id,它可以额表示任何对象,实际上应该是利用了指针地址的通用性,由于OC中操作对象都必须是指针,而指针本身又是一串地址,所以通过指针可以指向一切不同的对象。


20.        反射(这个和异常一样,感觉跟java也很像)


21.        选择器(其实就是C中的函数指针,以及java中的method类)


22.        类别(category),扩充类的功能,但不可以声明新的成员变量。

类别的应用比较广泛,譬如:第三方的OC库RegexKitLite就是对NSString, NSMutableString做的类别,为OC的字符类型增加了正则表达式的功能。


23.        协议@protocol,这个就是前面说的,相当于java中的interface,思想都是一中约定、规范,知识具体语法不一样罢了。


24.        终于到了内存管理了,擦,没怎么看懂,要好好找个专题弄懂。


25.        字符串

-(BOOL)isEquaToString: (NSString*)s

这与java的StringBuffer的append没什么区别。


26.        数组

Cocoa使用NSArray表示数组,但是它不能存储基本数据类型, enum, struct, nil --只能存储OC的对象。

NSArray *array = [NSArray arrayWithObjects: @”One”, @”Two”,@”Three”, nil];

从这个类方法arrayWithObjects的定义可以看出,它使用nil表示数组元素结束,这就是nil不能存储在NSArray中的原因。

NSMutableArray为长度可变的数组,相当于java中的list:

NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:10];
[mArray addObject: @”Apple”]; //添加数组元素
NSEnumerator *e = [mArray objectEnumerator]; //获取数组的迭代器,相当于java的Iterator

reserveObjectEnumerator用于获取反转后的数组迭代器。与java一致的地方是你在使用迭代器时,不能对数组进行添加、删除操作。

for (NSString *ms in mArray){
   NSLog(@”%@”, ms)
}

27.        字典(哈希表)

NSDictionary用于存储key-value的数据结构,与java中的map类似。

NSDictionary *dic = [NSDictionarydictionaryWithObjectsAndKeys: @”Apple”, @”A”, @”Google”, @”G”, nil]; //dictionaryWithObjectAndKeys后的可变参数,每两个为一个value-key,以nil表示结束。<pre name="code" class="objc">NSLog(@”%@”,[dic objectForKey:@”A”]); //按照指定的key查询value
 



同样地,有NSMutableDictionary,表示长度可变的字典。

NSMutableDictionary * mDic = [NSMutableDictionarydictionaryWithCapacity: 10];
[mDic setObject: @”Apple” forKey: @”A”];
for(id key in mDic){
   NSLog(@”%@ : %@”,key, [mDic objectForKey: key]);
} // 快速迭代的for-each循环
  

28.        哈希set

NSSet表示以hash方式计算存储位置的集合,与java中的Hashset是一致的。在NSSet中的每个对象都有一个未知的hash值,重复的对象将只能保留一个。因此,者引出了OC的对象比较问题,这需要你实现从NSObject继承而来的如下两个方法:


- (BOOL) isEqual: (id) anObject;

- (NSUInteger) hash;


这与java的对象比较没有什么区别,两个相等的对象必须有相同的hashCode,所以这两个方法必须同时实现。同样的,NSMutableSet表示长度可变的哈希Set。


29.        封装类(相当于java的包装器)

前面几个容器类的对象都不能存放基本数据结构、enum、struct、nil,怎么办呢?在java中我们知道所有的基本数据类型都有对应的封装类,例如:int—Integer、boolean—Boolean,使用封装类可以把基本数据类型包装位对象,而封装本身又有方法可以返回原来的基本数据类型。Cocoa使用NSValue作为封装类。


NSRect rect = NSMakeRect(1,2,30,50)
NSValue *v = [NSValue valueWithBytes: &rect objType:@encode(NSRect)]; // valueWithBytes要求传入包装数据的地址,也就是变量中存储的数据的首地址,我们依然使用c语言的&作为地址运算符,计算指针存储的首地址。</p><p>//objCType要求传入用于描述数据的类型、大小的字符串,使用@encode指令包装数据所属的类型
NSRect rect = NSMakeRect(1,2,30,50)
 
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:3];
[mArray addObject: v];
NSRect rect2;
[[mArray objectAtIndex: 0]getValue: &rect2];
//NSValue的-(void)getValue:(void*)value方法可以讲NSValue中的数据内容去除,要求传入的参数是一个指针,也就是说getValue方法将你传入的指针指向存储的数据。
//一般Cocoa中的getXXX方法都是这个作用,这也是为什么前面的getter方法不要以get作为方法前缀的原因。


对于基本数据类型,你可以使用简便的NSNumber来封装,它是NSValue的子类。

如果你想存储空值到集合类,可以使用NSNull *n = [NSNull null];的方式创建。


30.        日期类型

Cocoa中使用的NSDate类型表示日期。


31.        数据缓冲区(其实就是java中的字节数组)

Cocoa中使用NSData类型来实现缓冲区,用于存储二进制的数据类型,譬如:从网络下载回来的文件等。


32.        写入和读取属性(简直就是java序列化和反序列化的翻版)

在iPhone的*.ipa文件中,你经常可以看到*.plist文件,它保存了程序的相关属性,叫做属性列表。其实它就是NSArray、NSDictionary、NSString、NSData持久化后的文件。

这几个类型都有一个成员方法writeToFile: (NSString *) file atomically: BOOL 用于将自己写入到一个文件。atomically为YES表示文件先存储到临时文件区,如果文件保存成功,在替换掉原始文件,这样的好处是可以防止在保存文件过程中出错,但是仅用于小文件,因为你相当于是在临时文件存储区也放了一份,在加上原始文件,就是两份文件,如果文件比较大,将会占用较多的用户的磁盘空间。

如果你要持久化的类型那个不是上述的数组、字典、缓冲区,那该怎么办呢?OC中也有和java一样的序列化、反序列化支持,使用NSCoding协议。NSCoding协议定义了如下两个方法:

-(void) encodeWithCoder: (NSCoder *)coder;
-(id) initWithCoder: (NSCoder*) decoder;

第一个方法相当于编码对象,第二个方法是解码回对象,也就相当于给类型定义了一个新的init方法而已。


33.        对象的复制

对象的复制就相当于java中的clone()方法,也就是对象的深度复制,所谓深度复制就是重新分配一个存储空间,并将原来对象的内容都复制过来,从这些描述可以看出,复制也会分配空间,那就是你要对复制出来的对象release,就是前面说过的alloc、new、copy操作创建的对象,要手工release。

OC中的一个对象是否可以被复制,要看它的类型是否遵循NSCopying协议,这个协议中有个复制方法 –(id)copyWithZone:(*NSZone)zone 需要我们去实现。


34.        多线程

OC的多线程编程与java语言极其类似,分为原始的线程操作、线程池两种,后者其实就是使用队列等机制对前者的封装。java也一样,原始的线程操作使用Thread类,线程池的操作使用java.util.concurrent.*中的类库。

OC中的NSThread类型表示线程,NSCondition用于执行同步操作,在功能和用法上相当于java中的java.util.concurrent.*包中的Lock对象。

另外,OC也支持@synchronized指令做代码同步,也发也和java中的很类似。

@synchronized(要加锁的对象){
   //同步执行的代码
}

 

另外比较有趣的是,如果你想更新UI上的某一个部件,就需要在发起的新线程里调用UI所在的主线程上的一个方法,新线程不能直接访问主线程的方法,需要在run方法中使用如下的方法:

-(void)performSelectorOnMainThread: (SEL)aSelectorwithObject: (id)arg waitUntilDone: (BOOL)wait

如果你对java的swing熟悉的话,你应该知道在UI界,大多数更新界面的都必须在一个单线程中执行,因为如果更新界面是多线程的话会很难处理的,java swing处理的方法是更新界面的代码放在专门更新UI的线程下执行,其实这里iOS也是这么一个思想。

OC使用NSOperation、NSOperationQueue两个类型实现线程池的操作,NSOperation就好比java中的Runnable,其中的main(名字有点儿怪)方法也就是你要执行的操作,NSOperationQueue是一个线程池队列,你只需要把NSOperation添加到NSOperationQueue,队列就会retain你的NSOperation,然后执行其中的操作,指导执行完毕。队列中会有多个操作,队列会按照你添加的顺序逐个执行,也就说队列中的任务是串行的。

跟java一样,如果线程池可以满足你的业务需求,尽量使用线程池,而不是原始的NSThread,因为使用线程池屏蔽了许多线程自身需要处理的问题,代码也更加简洁。


35.        KVC与KVO(这个话题真高级)

KVC是NSKeyValueCoding的缩写,它是Foundation Kit中的一个NSObject的Category,作用你可类比java中的反射机制,就是动态访问一个对象中的属性。

KVC在解析key的字符串的时候,是会比你正常调用setter、getter要慢的,而且编译器无法对你的方法调用作出调查(因为你的属性名都是字符串,只有运行时才会知道你有没有写错),出错的几率也会提高,所以请不要随意使用KVC,而省去setter、getter方法。KVC一般用于动态绑定,也就是运行时才能确定谁调用哪个方法,编译器并不确定。

KVO就是NSKeyValueObservation的缩写,它也是Foundation Kit中的一个NSObject的Category,KVO基于KVC实现,计划于观察者设计模式(Observer Pattern)实现的一种通知机制,你可以类比java中的JMS,通过订阅的方式,实现了两个对象之间的解耦,但又可以让他们互相调用。(具体请查资料)


36.        谓词NSPredicate

Cocoa提供了NSPredicate用于指定过滤条件,谓词是指在计算机中表示计算真假值的函数,它使用起来有点儿像SQL的查询条件,主要用于从集合中分拣出符合条件的对象,也可以用于字符串的正则匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值