Objective-C 基础语法

方法调用(Calling Methods)

为了能够尽快上手,我们先来看一些简单的例子。Objective-C语法里面基本的方法调用是这样的:


  1. [object method];  
  2.  
  3. [object methodWithInput:input];  
  4.  

对象的方法可以返回值:


  1. output = [object methodWithOutput];  
  2.  
  3. output = [object methodWithInputAndOutput:input];  
  4.  

我们也可以在类里面调用如何创建对象的方法。下面的这个例子里面,我们调用了NSString类的string方法:


  1. id myObject = [NSString string];  
  2.  

id的类型意味着myObject这个变量可以指向任意类型的变量。当我们编译这个应用程序的时候,并不知道他实现的真实的类和方法。

在这个例子里面,很明显这个对象的类型应该是NSString,所以我们可以改一下他的类型:


  1. NSString* myString = [NSString string];  
  2.  

现在myString就是一个NSString类型的变量。这个时候假如我们试图使用一个NSString没有实现的方法时,编译器就会警告我们。

一定要注意在对象类型的右边有一个星号。所有的Objective-C对象变量都是指针类型的。id类型已经预先被定义成一个指针类型了。所以我们不需要再加星号。

嵌套消息调用(Nested Messages)

在许多编程语言里面嵌套消息,或者嵌套函数看起来就像这样:


  1. function1 ( function2() );  

function2的返回值被传递给function1当输入参数。在Objective-C里面,嵌套消息调用就像这样:


  1. [NSString stringWithFormat:[prefs format]];  

我们应该尽量避免在一行代码里面嵌套调用超过两个。因为这样的话,代码的可读性就不太好。 

多参输入的方法(Multi-Input Methods)

多个输入参数的方法。在Objective-C里面,一个方法名可以被分割成几段。在头文件里面,就应该这样子来定义一个多输入参数的方法:


  1. -(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;  
  2.  

我们这样来调用它:


  1. BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];  
  2.  

参数不一定要给它命名。在运行期系统里面这个方法真实的名字叫writeToFile:atomically:。


  1. Accessors(Getter & Setter)  
  2.  

在Objective-C里面所有的实例对象默认都是私有的。所有在大多数情况下我们需要用accessors去读取或者设置变量的值。有两个语法都支持这样的操作,这个时传统的老的语法:


  1. [photo setCaption:@"Day at the Beach"];  
  2. output = [photo caption];  
  3.  

第二行的代码其实并非直接去读对象实例的变量。事实上它调用的是名叫caption的方法。在Objective-C里大多数情况下我们不需要给getters加get的前缀。

无论什么时候我们见到方括号,其实我们都是向一个对象或者一个类发送了一个消息。

Dot Syntax

在Objective-C 2.0里面,新增加了一个"."操作的语法。在Mac OS X 10.5里面就使用了Objective-C 2.0语法:


  1. photo.caption = @"Day at the Beach";  
  2. output = photo.caption;  
  3.  

我们两种方式都可以使用。但是在一个工程里面最好保持风格一致,只使用某一种。"."操作只能够被使用在setters和getters里面,而不能用在一般意思的方法上。

创建对象

主要有两种方式来创建一个对象。第一种办法像这面这样:


  1. NSString* myString = [NSString string];  
  2.  

这是一种非常习惯性的风格。在这种方式情况下,我们创建的是系统自动释放(autoreleased)类型的对象。关于自动释放类型autoreleased,我们以后会深入讨论一下。然而在许多情况下,我们需要手动的去创建对象:


  1. NSString* myString = [[NSString alloc] init];  
  2.  

这是一个嵌套的方法调用。第一个调用的NSString自己的alloc方法。这是一个相对比较底层的调用,因为他创建了内容,以及实例化了一个对象。

第二块代码调用了新创建对象的init方法。这个init方法实现了比较常用的基本设置,比如创建实例对象的参数。对于一般开发人员而言,实现这个客户的类的具体的细节并不清楚。

在一些情况下,我们可以用不通的初始化方式去赋初值:


  1. NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];  
  2.  

基本的内存管理

假如我们正在为Mac OS X开发一个应用程序,我们可以选择是否启用垃圾回收机制。这就意味着我们不需要去考虑内存管理,除了一个特别复杂的情形我们需要处理一下。

然而,我们有的时候我们的开发环境没有垃圾回收机制,比如iPhone开发的时候就没有垃圾回收机制。在这种情况下,我们就需要了解一些基本的内存管理方面的概念。

假如我们手动的通过alloc创建了一个对象,我们需要用完这个对象后release它。我们不需要手动的去release一个autoreleased类型的对象,假如真的这样去做的话,我们的应用程序将会crash。

这里有两个例子:


  1. // string1 will be released automatically  
  2.  
  3. NSString* string1 = [NSString string];  
  4.  
  5. // must release this when done  
  6.  
  7. NSString* string2 = [[NSString alloc] init];  
  8.  
  9. [string2 release];  
  10.  

就这个教程而言,我们可以人为autoreleased对象会在当前函数方法调用完成后被释放。

当然了,还有很多关于内存管理的只是我们需要学习,但是这需要我们了解更多的基本概念以后才能去涉及。 

设计一个类的Interface

就Objective-C语言而言,创建一个类非常简单。它非常典型的分成了两个部分。

类的接口通常保存在ClassName.h文件里,它定义了实例的参数,以及一些公开的方法。

类的实现在ClassName.m文件里。它包含了真正运行的代码和那些方法。它还经常定义一些私有的方法。这些私有的方法对于子类是不可见的。

这里有一个接口文件的大概。类名Photo,所以文件名叫Photo.h:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. @end  
  12.  

首先,我们把Cocoa.h import进来。Cocoa的应用程序的所有的基本的类大多都是这样做的。#import宏指令会自动的避免把同一个文件包含多次。

@interface符号表明这是Photo类的声明。冒号指定了父类。上面这个例子父类就是NSObject。

在大括弧里面,有两个变量:caption和photographer。两个都是NSString类型的。当然了,他们也可以是任何别的类型包括id类型的。

最后@end结束整个声明。

增加方法

让我们为成员变量加一些getters:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. - caption;  
  12.  
  13. - photographer;  
  14.  
  15. @end  
  16.  

别忘记,Objective-C方法不需要加get前缀。一个单独小横杆表明它是一个实例的方法。假如是一个加号的话,那就说明它是一个类的方法。

编译器默认的方法的返回类型为id。还有所有的方法的参数的默认类型也都是id类型的。所以上面的代码从技术上讲是对的。但是很少这么用。我们还是给它加上返回类型吧:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. - (NSString*) caption;  
  12.  
  13. - (NSString*) photographer;  
  14.  
  15. @end  
  16.  

下面我们再加上setters:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. - (NSString*) caption;  
  12.  
  13. - (NSString*) photographer;  
  14.  
  15. - (void) setCaption: (NSString*)input;  
  16.  
  17. - (void) setPhotographer: (NSString*)input;  
  18.  
  19. @end  
  20.  

Setters不需要返回任何值,所以我们把它的类型指定为void. 

类的实现

我们通过实现getters来创建一个类的实现:


  1. #import "Photo.h"  
  2.  
  3. @implementation Photo  
  4.  
  5. - (NSString*) caption {  
  6.  
  7. return caption;  
  8.  
  9. }  
  10.  
  11. - (NSString*) photographer {  
  12.  
  13. return photographer;  
  14.  
  15. }  
  16.  
  17. @end  
  18.  

这部分的代码由@implementation再来加上类名开始,以@end结束。就跟类的接口定义一样,所有的方法跟接口定义里的一样。所有的对象都必要既要定义也要实现。

假如我们以前也写过代码的话,Objective-C里面的getters看上去跟别的差不多。所以我们下面就来介绍setters,它需要一点说明。


  1. - (void) setCaption: (NSString*)input  
  2.  
  3. {  
  4.  
  5. [caption autorelease];  
  6.  
  7. caption = [input retain];  
  8.  
  9. }  
  10.  
  11. - (void) setPhotographer: (NSString*)input  
  12.  
  13. {  
  14.  
  15. [photographer autorelease];  
  16.  
  17. photographer = [input retain];  
  18.  
  19. }  
  20.  

每个setter处理两个变量。第一个是当前存在对象的应用。第二个是新的输入对象。在支持垃圾回收的开发环境里,我们只要直接赋新值就可以了:


  1. - (void) setCaption: (NSString*)input {  
  2.  
  3. caption = input;  
  4.  
  5. }  
  6.  

但是假如我们不可以用垃圾回收机制的话,我们就需要先retain旧的对象,然后retain新的对象。

有两种方法可以释放一个引用对象:release 和 autorelease。标准的release会直接删除引用。autorelease方法会在将来的某个时候去release它。在它声明周期结束前,它会毫无疑问的存在。在本例中,上面setPhotographer中的photographer对象,将会在函数结束的时候被释放。

在setter里面用autorelease是安全的,因为新对象跟老的对象有可能是同一个对象有可能指向的是同一个对象。对于一个我们即将retain的对象,我们不应该立即release它。

这个也许现在看起来会困惑,但是随着我们的学习,会越来越能理解它。现在我们不需要立刻完全理解它。

初始化

我们可以创建一个初始化方法去给类的实例的成员变量赋初值:


  1. - (id) init  
  2.  
  3. {  
  4.  
  5. if ( self = [super init] )  
  6.  
  7. {  
  8.  
  9. [self setCaption:@"Default Caption"];  
  10.  
  11. [self setPhotographer:@"Default Photographer"];  
  12.  
  13. }  
  14.  
  15. return self;  
  16.  
  17. }  
  18.  

上面的代码感觉没啥好解释的,虽然第二行代码好像看上去没啥用。这个是一个单等于号,就是把[super init]的值赋给了self。

它基本上是在调用父类去实现它的初始化。这个if代码段是设置默认值之前验证初始化是否成功。

释放资源Dealloc

这个dealloc方法是在当一个对象希望被从内容里面删除的时候调用。这个我们释放在子类里面引用成员变量的最好的时机:


  1. - (void) dealloc  
  2.  
  3. {  
  4.  
  5. [caption release];  
  6.  
  7. [photographer release];  
  8.  
  9. [super dealloc];  
  10.  
  11. }  
  12.  

开始两行我们发送了release通知给了两个成员变量。我们不要在这里用autorelease。用标准的release更快一点。

最后一行的[super dealloc];非常重要。我们必须要发送消息去让父类清除它自己。假如不这么做的话,这个对象其实没有被清除干净,存在内存泄露。

dealloc在垃圾回收机制下不会被调用到。取而代之的是,我们需要实现finalize方法。 

更多关于内存管理

Objective-C的内存管理系统基于引用记数。所有我们需要关心的就是跟踪我们引用,以及在运行期内是否真的释放了内存。

用最简单的术语来解释,当我们alloc一个对象的时候,应该在某个时候retain了它。每次我们调用了alloc或者retain之后,我们都必须要调用release。

这就是引用记数理论。但是在实践的时候,只有两种情况我们需要创建一个对象:

1. 成为一个类的成员变量

2. 只临时的在一个函数里面被使用

在更多的时候,一个成员变量的setter应该仅仅autorelease旧的对象,然后retain新的对象。我们只需要在dealloc的时候调用release就可以了。

所以真正需要做的就是管理函数内部的local的引用。唯一的原则就是:假如我们alloc或者copy了一个对象,那么我们在函数结束的时候需要release或者autorelease它。假如我们是通过别的方式创建的,就不管。

这里是管理成员对象的例子:


  1. - (void) setTotalAmount: (NSNumber*)input  
  2.  
  3. {  
  4.  
  5. [totalAmount autorelease];  
  6.  
  7. totalAmount = [input retain];  
  8.  
  9. }  
  10.  
  11. - (void) dealloc  
  12.  
  13. {  
  14.  
  15. [totalAmount release];  
  16.  
  17. [super dealloc];  
  18.  
  19. }  
  20.  

这里是本地引用的例子。我们只需要release我们用alloc创建的对象:


  1. NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];  
  2.  
  3. NSNumber* value2 = [NSNumber numberWithFloat:14.78];  
  4.  
  5. // only release value1, not value2  
  6.  
  7. [value1 release];  
  8.  

这里是用本地引用对象去设一个成员变量的例子:


  1. NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];  
  2.  
  3. [self setTotal:value1];  
  4.  
  5. NSNumber* value2 = [NSNumber numberWithFloat:14.78];  
  6.  
  7. [self setTotal:value2];  
  8.  
  9. [value1 release];  
  10.  

注意到如何管理本地引用其实都是一样的。不管你是否把它设给了一个成员变量。我们无须考虑setters的内部实现。

如果我们很好的理解了这些的话,我们基本上理解了80%的Objective-C内存管理方面的内容了。 

属性Properties

前面我们写caption和author的accessors的时候,你可以已经注意到了代码非常简明,应该可以被抽象提取出来。

属性在Objective-C里是一个新的功能。他可以让我们自动的生成accessors,另外还有一些别的优点。我们可以把上面Photo的类转成用属性来实现:

上面那个类原先的实现是这样:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. - (NSString*) caption;  
  12.  
  13. - (NSString*) photographer;  
  14.  
  15. - (void) setCaption: (NSString*)input;  
  16.  
  17. - (void) setPhotographer: (NSString*)input;  
  18.  
  19. @end  
  20.  

假如用属性来实现就是这样:


  1. #import  
  2.  
  3. @interface Photo : NSObject {  
  4.  
  5. NSString* caption;  
  6.  
  7. NSString* photographer;  
  8.  
  9. }  
  10.  
  11. @property (retain) NSString* caption;  
  12.  
  13. @property (retain) NSString* photographer;  
  14.  
  15. @end  
  16.  

@property是Objective-C来声明属性的编译指令。括号里面的"retain"指明了setter需要retain输入的对象。这行其他的部分指定了属性的类型以及名字。

下面让我们来看看这个类的实现:


  1. #import "Photo.h"  
  2.  
  3. @implementation Photo  
  4.  
  5. @synthesize caption;  
  6.  
  7. @synthesize photographer;  
  8.  
  9. - (void) dealloc  
  10.  
  11. {  
  12.  
  13. [caption release];  
  14.  
  15. [photographer release];  
  16.  
  17. [super dealloc];  
  18.  
  19. }  
  20.  
  21. @end  
  22.  

@synthesize指令自动的生成了我们的setters和getters。所以我们只需要实现类的dealloc方法。

Accessors只有当他们原先没有的时候,才会被生成。所以可以放心大胆的去用@synthesize来指定属性。而且可以随意实现你自己的getter和setter。编译器会自己去找哪个方法没有。

属性声明还有别的选项,但是限于篇幅层次,我们下次再介绍。

Logging

在Objective-C里,往console写日记非常简单。事实上NSLog()跟C语言的printf()两个函数几乎完全相同,除了NSLog是用额外的“%@”去获得对象。


  1. NSLog ( @"The current date and time is: %@", [NSDate date] );  
  2.  

我们可以log一个对象到console里去。NSLog函数调用要输出对象的description方法,然后打印返回的NSString。我们可以在自己的类里重写description方法,这样我们就可以得到一个自定义的字符串。 

调用nil对象的方法(Calling Methods on Nil)

在Objective-C里,nil对象被设计来跟NULL空指针关联的。他们的区别就是nil是一个对象,而NULL只是一个值。而且我们对于nil调用方法,不会产生crash或者抛出异常。

这个技术被framework通过多种不同的方式使用。最主要的就是我们现在在调用方法之前根本无须去检查这个对象是否是nil。假如我们调了nil对象的一个有返回值的方法,那么我们会得到一个nil返回值。

我们可以通过nil对象让我们的dealloc函数实现看上去更好一些:


  1. - (void) dealloc  
  2. {  
  3. self.caption = nil;  
  4. self.photographer = nil;  
  5. [super dealloc];  
  6. }  
  7.  

之所以可以这么做是因为我们给把nil对象设给了一个成员变量,setter就会retain nil对象(当然了这个时候nil对象啥事情也不会做)然后release旧的对象。这个方式来释放对象其实更好,因为这样做的话,成员变量连指向随机数据的机会都没有,而通过别的方式,出现指向随机数据的情形机会不可避免。

注意到我们调用的self.VAR这样的语法,这表示我们正在用setter,而且不会引起任何内存问题。假如我们直接去设值的话,就会有内存溢出:


  1. // incorrect. causes a memory leak.  
  2. // use self.caption to go through setter  
  3. caption = nil;  
  4.  

Categories

Categories是Objective-C里面最常用到的功能之一。 基本上category可以让我们给已经存在的类增加方法,而不需要增加一个子类。而且不需要知道它内部具体的实现。

如果我们想增加某个framework自带的类的方法,这非常有效。如果我们想在我们程序工程的NSString能够增加一个方法,我们就可以使用category。甚至都不需要自己实现一个NSString的子类。

比如,我们想在NSString里面增加一个方法来判断它是否是一个URL,那我们就可以这么做:


  1. #import  
  2.  
  3. @interface NSString (Utilities)  
  4.  
  5. - (BOOL) isURL;  
  6.  
  7. @end  
  8.  

这跟类的定义非常类似。区别就是category没有父类,而且在括号里面要有category的名字。名字可以随便取,但是习惯叫法会让人比较明白category里面有些什么功能的方法。

这里是具体的实现。但是要注意,这本身并不是一个判断URL很好的实现。我们主要是为了整体的了解category的概念。


  1. #import "NSString-Utilities.h"  
  2.  
  3. @implementation NSString (Utilities)  
  4.  
  5. - (BOOL) isURL  
  6.  
  7. {  
  8.  
  9. if ( [self hasPrefix:@"http://"] )  
  10.  
  11. return YES;  
  12.  
  13. else  
  14.  
  15. return NO;  
  16.  
  17. }  
  18.  
  19. @end  
  20.  

现在我们可以在任何的NSString类对象里都可以调用这个方法了。下面的代码在console里面打印的"string1 is a URL":


  1. NSString* string1 = @"http://www.CocoaDev.cn/";  
  2.  
  3. NSString* string2 = @"Pixar";  
  4.  
  5. if ( [string1 isURL] )  
  6.  
  7. NSLog (@"string1 is a URL");  
  8.  
  9. if ( [string2 isURL] )  
  10.  
  11. NSLog (@"string2 is a URL");  
  12.  

跟子类不一样,category不能增加成员变量。我们还可以用category来重写类原先的存在的方法,但是这需要非常非常小心。

记住,当我们通过category来修改一个类的时候,它对应用程序里的这个类所有对象都起作用。

时间: 2024-09-17 03:48:19

Objective-C 基础语法的相关文章

【Java】基础语法

虽然说Java又是一门新的编程语言,但基础的语法部分还都是可以和之前学习的编程语言一样的. 既然被称之为语法,那就必然有一定的规则.其中,在标识符和关键字部分,我们就可以很明显的看出一些强制性的要求. 关键字不能作为标识符,标识符为小写字母等,这有着一系列的规则,然而我们也并不需要去记,按着常规走,结合自身的以往的一些经验,就没什么问题. 学习了这么久的编程语言,也是总结过很多次关于数据的知识点.而这一次,一层一层,把它们之间的关系联系的更紧密了. 数据在程序中是少不了的.而我们如何获取数据或者

jsp基础语法 一 scriptlet

jsp作为WEB的开发基础,有其重要的地位,那么熟练掌握JSP的语法及应用就成了重中之重. 首先我们一起先从JSP的基本语法学起:(以下内容来自李兴华视频手稿整理) scriptlet简介 script表示的是脚本小程序,像之前out.println()这个语句是缩写在<%%>之中的,很明显,这里面 缩写的语句就是一个script. 在jsp中最重要的部分就是Scriptlet(脚本小程序),所有嵌入在HTML代码中的java程序都必须使用 Scriplet标记出来,在jsp中一共有三种scr

jsp基础语法 六 jsp+jdbc访问数据库

学习过了jsp基础语法以及HTML和javascript的用法之后,jsp+jdbc连接数据库开发动态WEB网页就可以实现了. 对于学过java SE的同学来说,数据库的操作并不陌生,如果有忘记的或者不会的可以到http://zhaoyuqiang.blog.51cto.com/6328846/1127658  学习. jsp中的数据库连接又是如何呢? 怎样才能利用上我们学过的HTML和javascript的知识呢? 这就是我们这一篇文章的学习内容----jsp+jdbc访问数据库. 我们就以一

jsp基础语法 五 跳转指令

跳转的作用: 使用跳转指令可以将一个用户的请求,从一个页面传递到另外一个页面. 例如:用户登录QQ空间,如果用户名和密码正确的话就会跳转到空间首页,否则就会跳转到失败页. 跳转指令的语法: 不传递参数   <jsp:forward page=""/> 传递参数:  <jsp:forward page=""> <jsp:param name="参数名称" value="参数内容"> </

jsp基础语法 四 包含指令

什么是包含: 在页面开发的时候,一些代码难免会有重复使用的情况,比如说A页面有一段代码a,B页面和C页面都能用到a这段代码,那么B和C页面就把A页面包含进来,自然的a这段代码也被包含进BC页面了,这样就实现了B.C页面能用到a代码了,避免了重复的书写a代码. 包含的意义: 使用包含操作,可以将一些重复的代码包含进来继续使用. 一般分为如上四个区域. 真正在改变的地方只是具体内容,因为根据不同的用户浏览的不同,那么肯定这上面会存在差别  现在就有两种做法实现以上一种功能 方法一:在没有一个jsp页

jsp基础语法 三 page指令

page指令 page指令是在jsp开发中较为重要,使用此属性,可以定义一个jsp页面的相关属性,包括 设置MIME类型.定义需要导入的包.错误页的指定等 page指令的语法:<%@ page 属性="内容"%> page的主要属性: 设置MIME 先来观察一道程序: <html> <head> <title> New Document </title> </head> <body> <cente

jsp基础语法 二 注释的使用

我的一个同学问我,java中有"//"这个样的注释,用起来挺方便的,jsp的注释难道不是这个吗?用"//"在jsp中不行. 对于学习了这么长时间的jsp,注释是最基本的.jsp中的注释不同于java. 在jsp中支持两种注释,一种是显示注释,这种注释客户端是允许看见的,另外一种注释是隐式注释,此种注释客户端是无法看见的. 显示注释语法: <!--注释内容--> 隐式注释语法: 格式一:// 注释,单行注释: 格式二:  /* 注释 */,多行注释: 格式

C#基础语法:方法参数详解

这篇文章主要介绍了C#基础语法:方法参数详解,本文讲解了值参数.引用参数.输出参数.参数数组等参数类型,并分别给出代码实例,需要的朋友可以参考下     ●值参数 :一个值参数相当于一个局部变量,当使用值参数的时候,将会分配一个新的存储位置,将实参拷贝到该位置,并将该拷贝值传递给该方法.因此,值参数只能将值带进方法,但是不能带出方法,而不会影响实参的值. ●引用参数:当使用引用参数的时候,将不会分配一个新的存储位置,In other words,引用参数能将值带进方法,也能带出方法,因而会影响实

C#基础语法:可空类型详解

  这篇文章主要介绍了C#基础语法:可空类型详解,本文分析了可空类型的源码.研究了可空类型强制转换为常规类型.可空类型的运算等内容,需要的朋友可以参考下 以下是System.Nullable在FCL中的定义. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

SQL左联右联内联全联基础语法

  sql内联语法基础语法(inner join) SELECT * FROM 表1 inner join 表2 on 表1.userid=表2.userid sql内联语法说明:上面的语句就是查询表1和表2两个表,只要两个表中的userid字段相同的话就拼成一行显示出来. 45it.com注:以上语法同等select * from 表1,表2 where 表1.userid=表2.userid,而使用sql内联的话,将会提高很多效率. sql左联语法基础语法(left outer join)