关于构造函数构造默认对象加括号的疑惑

以前一直未注意 构造函数在构造默认对象时,如果无参数传递绝不应该加括号

class  TEST

{

public:

      TEST(int){}

      TEST(){}

      void fun(){}

};

 

int main()

{

     TEST t(10); //正确,传进参数10

     TEST t();  //原意是利用默认构造函数构造一个默认的对象,事实上相当于声明一个函数t(),返回值为TEST,编译自然通过

     t.fun();  //编译出错,t不是一个类或结构体

     return 0;

}

 

 

很小的问题 可是现在才发现.....汗

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
基本数据类型的包装类 •八大数据类型的包装类分别为:Byte、Short、Integer、Long、Character、 Float、Double、Boolean。 把基本数据类型变量包装类实例是通过对应包装类的构造器来实现的,不仅如此,8个包装类中除了 Character之外,还可以通过传入一个字符串参数来构建包装类对象。 •如果希望获得包装类对象中包装的基本类型变量,则可以使用包装类提供的XxxValue()实例方法。 自动装箱与自动拆箱 •JDk还提供了自动装箱和自动拆箱。自动装箱就是把一个基本类型的变量直接赋给对应的包装类变量,自动拆箱 则与之相反。 •包装类还可以实现基本类型变量和字符串之间的转换,除了Character之外的所有包装类都提供了一个 parseXxx(String s)静态方法。 •如果将基本类型转换为这符串,只需在后面+ “”进行连接运算。 Java 7对包装类的增强 •Java 7为所有包装类增一个新方法: compare(x , y)的方法。该方法用于比较两个包装类实例,当x>y, 返回大于0的数;当x==y,返回0;否则返回小于0的数。 对象的方法 •打印对象和toString方法:toString方法是系统将会输出该对象的“自我描述”信息,用以告诉外界对象具有的状 态信息。 •Object 类提供的toString方法总是返回该对象实现类的类名 + @ +hashCode值。 •==和equals比较运算符:==要求两个引用变量指向同一个对象才会返回true。equals方法则允许用户提供自 定义的相等规则。 •Object类提供的equals方法判断两个对象相等的标准与==完全相同。因此开发者通常需要重写equals方法。 类成员 •在java类里只能包含Field,方法,构造器,初始化块,内部类(接口、枚举)等5种成员。 用static修饰的类成员属 于类成员,类Field既可通过类来访问,也可以通过类的对象来访问。当通过对象来访问类属性时,系统会在底 层转换为通过该类来访问类 属性。 类成员规则 •类成员并不是属于实例,它是属于类本身的。只要类存在,类成员就存在。 •即使通过null对象来访问类成员,程序也不会引发NullPointerException。   类成员不能访问实例成员。 单例类 •如果一个类始终只能创建一个对象,称为单例类。须符合以下几个条件:   –1.我们把该类的构造器使用Private修饰,从而把该 类的所有构造器隐藏起来。   –2.则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且必须使用static修饰   –3.该类还必须缓存已经创建的对象,必须用static修饰 final变量 •final修饰变量时,表示该变量一旦获得 初始值之后就不可被改变。 •final既可修饰成员变量,也可以修饰局部变量。 final修饰成员变量 •成员变量是随类的初始化或对象初始化而初始化的。final修饰的成员变量必须由程序员指定初始值。 •对于类属性而言,要么在静态初始化中初始化,要么在声明该属性时初始化。 •对于实例属性,要么在普通初始化块中指定初始值。要么在定义时、或构造器中指定初始值。 final修饰局部变量 •使用final修饰局部变量时既可以在定义时指定默认值,也可以不指定默认值。 •给局部变量赋初始值,只能一次,不能重复。 final修饰基本类型和引用类型 •当使用final修饰基本数据类型时,不能对其重新赋值,不能被改变。 •但对引用类型的变量而言,它仅仅保存的是一个引用,final只能保证他的地址不变,但不能保证对象,所以引用 类型完全可以改变他的对象。 可执行“宏替换”的final变量 •对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足3个条件,这个final变量就 不再是一个变量,而是相当于一个直接量。   –使用final修饰符修饰;   –在定义该final变量时指定了初始值;   –该初始值可以在编译时就被确定下来。 final方法 •final方法 •final 修饰的方法不可以被重写。 •final 修饰的方法仅仅是不能重写,但它完全可以被重载。 •final 修饰的类不可以被继承 不可变的类 •不可变的类要满足以下几个条件:   –1.使用private和final修饰符来修饰该类的属性   –2.提供带参数的构造器,用于根据传入参数来初始化类里的属性   –3.仅为该类的属性提供getter方法,不要为该类的属性提供setter方法,因为普通方法无法修改final修饰的 属性   –4.如有必要,重写Object类中hashCode 和equals •缓存实例的不可变类:如果程序经常需要使用不可变类的实例,则可对实例进行缓存。 抽象方法和抽象类 •抽象方法和类都必须使用abstract来修饰,有抽象方法的类只能定义成抽象类,抽象里也可以没有抽象方法。 • 抽象类不能被实例化,可以通过其子类给他赋值,普通类里有的抽象里也有,定义抽象方法只需在普通方法上增 abstract修饰符,并把普通方法的方法体(也就是方法后花括号括起来的部分)全部去掉,并在方法后增分号 即可。 抽象类的特征 •抽象类的特征:有得有失,得到了新能力,可以拥有抽象方法;失去了创建对象的能力。 抽象类的作用 •抽象类代表了一种未完成的类设计,它体现的是一种模板。 •抽象类与模板模式。 接口的概念 •接口定义的是多个类共同的行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用的 方法。 •接口体现了规范与实现分离的设计。 接口的定义 •和类定义不同,定义接口不再用class关键字,而是使用interface关键字。语法如下: •[修饰符] interface接口名 extends 父接口1,父接口2 ... •{ • 零个到多个常量定义... • 零个到多个抽象方法定义... • 零个到多个内部类、接口、枚举定义... • 零个到多个默认方法或类方法定义... •} 接口里的成分 •在定义接口时,接口里可以包含成员变量(只能是常量),方法(只能是抽象实例方法、类方法或默认方法),内 部类(包括内部接口、枚举类   –常量都是:public static final修饰   –方法都是:public abstract 修饰   –内部的类:public static 接口的继承 •接口的继承和类继承不一样,接口完全支持多继承,子接口扩展某个父接口将会获得父接口的所有抽象方法,常 量属性,内部类和枚举类定义。 使用接口 •接口可以用于声明引用类型的变量,但接口不能用于创建实例。 •当使用接口来声明引用类型的变量时,这个引用类型的变量必须引用到其实现类的对象。 •一个类可以实现一个或多个接口,继承使用extends关键字,实现接口则使用implements关键字。 实现接口 •一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽 象方法); •否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。 接口和抽象类的相似性 •接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。 •接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。 接口与抽象类的区别 •接口里只能包含抽象方法,不同包含已经提供实现的方法;抽象类则完全可以包含普通方法。 •接口里不能定义静态方法;抽象类里可以定义静态方法。 •接口里只能定义静态常量属性,不能定义普通属性;抽象类里则既可以定义普通属性,也可以定义静态常量属 性。 •接口不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而让其子类调用这些构 造器来完成属于抽象类的初始化操作。 •接口里不能包含初始化块,但抽象类则完全可以包含初始化块。 •一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补 Java单继承的不足。 面向接口编程 •接口体现了规范与实现分离的原则。充分利用接口可以很好地提高系统的可扩展性和可维护性。 •接口与简单工厂模式、命令模式等。 内部类 •我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类,有的也叫嵌套类,包含内   部类的类也被称为外部类有的也叫宿住类。 •内部类提供了更好的封装,内部类成员可以直接访问外部类的私有数据,因为内部类被当成其他外部类成员。 •匿名内部类适合用于创建那些仅需要一次使用的类。 非静态内部类 •定义内部类非常简单,只要把一个类放在另一个类内部定义即可。 •当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,如果存在该 名字的局部变量,就使用该变量,如果不存在,则到该方法所在的内部类中查找是否存在该名字的属性,如果存在 则使用该属性。 •总之,第一步先找局部变量,第二步,内部类的属性,第三步。外部类的属性。 本文原创作者:pipi-changing 本文原创出处:http://www.cnblogs.com/pipi-changing/ 静态内部类 •如果用static修饰一个内部类,称为静态内部类。 •静态内部类可以包含静态成员,也可以包含非静态成员。所以静态内部类不能访问外部类的实例成员,只能访问   外部类的类成员。 •静态内部类的对象寄存在外部类里,非静态内部类的对象寄存在外部类实例里 使用内部类 •1.在外部类内部使用内部类-不要在外部类的静态成员中使用非静态内部类,因为静态成员不能访问非静态成 员。 • 2.在外部类以外使用非静态内部类。   –private 修饰的内部类只能在外部类内部使用。   –在外部类以外的地方使用内部类,内部类完整的类名应该OuterClass.InnerClass.   –在外部类以外的地方使用非静态内部类创建对象的语法如下:OuterInstance.new InnerConstructor()   –在外部类以外的地方使用静态内部类创建对象的语法如下:new OuterClass.InnerConstructer(); 局部内部类 •如果把一个内部类放在方法里定义,这就是局部内部类,仅仅在这个方法里有效。 •局部内部类不能在外部类以外的地方使用,那么局部内部类也不能使用访问控制符和static修饰 匿名内部类 •匿名内部类适合创建那种只需要一次使用的类,定义匿名内部类的语法格式如下: •new 父类构造器(实例列表) |实现接口) •{ • //匿名内部类的 类体部分 •} •匿名内部类不能是抽象类,匿名内部类不能定义构造器。 Lambda表达式入门 •Lambda表达式主要作用就是代替匿名内部类的繁琐语法。它由三部分组成:   –形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。   –箭头(->),必须通过英文等号和大于符号组成。   –代码块。如果代码块只有包含一条语句,Lambda表达式允许省略代码块的花括号,如果省略了代码块的花括 号,这条语句不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。 Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的 值。 Lambda表达式与函数式接口 •如果采用匿名内部类语法来创建函数式接口的实例,只要实现一个抽象方法即可,在这种情况下即可采用 Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。 •Lambda表达式有如下两个限制:   –Lambda表达式的目标类型必须是明确的函数式接口。   –Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽 象方法的接口(函数式接口)创建对象。 •为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式:   –将Lambda表达式赋值给函数式接口类型的变量。   –将Lambda表达式作为函数式接口类型的参数传给某个方法。   –使用函数式接口对Lambda表达式进行强制类型转换。 方法引用与构造器引用 种类 示例 说明 对应的Lambda表达式 引用类方法 类名::类方法 函数式接口中被实现方法的全部参数传给该类方法作为参数。 (a,b,...) -> 类名.类方法(a,b, ...) 引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传给该方法作为参数。 (a,b, ...) -> 特定对象.实例方法(a,b, ...) 引用某类对象的实例方法 类名::实例方法 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数。 (a,b, ...) ->a.实例方法(b, ...) 引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数。 (a,b, ...) ->new 类名(a,b, ...) Lambda表达式与匿名内部类 •Lambda表达式与匿名内部类存在如下相同点:   –Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变 量(包括实例变量和类变量)。   –Lambda表达式创建的对象与匿名内部类生成的对象一样, 都可以直接调用从接口继承得到的默认方法。 •Lambda表达式与匿名内部类主要存在如下区别:   –匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方 法即可。但Lambda表达式只能为函数式接口创建实例。   –匿名内部类可以为抽象类、甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。   –匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调 用接口中定义的默认方法。 手动实现枚举类 •可以采用如下设计方式:   –通过private将构造器隐藏起来。   –把这个类的所有可能实例都使用public static final属性来保存。   –如果有必要,可以提供一些静态方法,允许其他程序根据特定参数来获取与之匹配实例。 JDK 5新增的枚举支持 •J2SE1.5新增了一个enum关键字,用以定义枚举类。正如前面看到,枚举类是一种特殊的类,它一样可以有自 己的方法和属性,可以实现一个或者多个接口,也可以定义自己的构造器。一个Java源文件中最多只能定义一个 public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。 枚举类 •枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是继承Object 类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang. Comparable两个接口。 •枚举类的构造器只能使用private访问控制符,如果省略了其构造器的访问控制符,则默认使用private修饰;如 果强制指定访问控制符,则只能指定private修饰符。 •枚举类的所有实例必须在枚举类中显式列出,否则这个枚举类将永远都不能产生实例。列出这些实例时系统会自 动添public static final修饰,无需程序员显式添。 •所有枚举类都提供了一个values方法,该方法可以很方便地遍历所有的枚举值。 枚举类的属性、方法和构造器 •枚举类也是一种类,只是它是一种比较特殊的类,因此它一样可以使用属性和方法。 •枚举类通常应该设计成不可变类,也就说它的属性值不应该允许改变,这样会更安全,而且代码更简洁。为 此,我们应该将枚举类的属性都使用private final 修饰。 •一旦为枚举类显式定义了带参数的构造器,则列出枚举值时也必须对应地传入参数。 实现接口的枚举类 •枚举类也可以实现一个或多个接口。与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也 需要实现该接口所包含的方法。 •如果需要每个枚举值在调用同一个方法时呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法,每个 枚举值提供不同的实现方式,从而让不同枚举值调用同一个方法时具有不同的行为方式。 包含抽象方法的枚举类 •可以在枚举类里定义一个抽象方法,然后把这个抽象方法交给各枚举值去实现即可。 •枚举类里定义抽象方法时无需显式使用abstract关键字将枚举类定义成抽象类,但因为枚举类需要显式创建枚举 值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。 垃圾回收机制 •垃圾回收机制只负责回收堆内存中对象,不会回收任何任何物理资源(例如数据库连接,网络IO等资源)。 •程序无法精确控制垃圾回收的运行,垃圾回收会在合适时候进行垃圾回收。当对象永久性地失去引用后,系统就 会在合适时候回收它所占的内存。 •垃圾回收机制回收任何对象之前,总会先调用它的finalize方法,该方法可能使该对象重新复活(让一个引用变量 重新引用该对象),从而导致垃圾回收机制取消回收 对象在内存中的状态 •激活状态:当一个对象被创建后,有一个以上的引用变量引用它。则这个对象在程序中处于激活状态,程序可通 过引用变量来调用该对象的属性和方法。 •去活状态:如果程序中某个对象不再有任何引用变量引用它,它就进入了去活状态。在这个状态下,系统的垃圾 回收机制准备回收该对象所占用的内存,在回收该对象之前,系统会调用所有去活状态对象的finalize方法进行资 源清理,如果系统在调用finalize方法重新让一个引用变量引用该对象,则这个对象会再次变为激活状态;否则该 对象将进入死亡状态。 •死亡状态:当对象与所有引用变量的关联都被切断,且系统会调用所有对象的finalize方法依然没有使该对象变成 激活状态,那这个对象将永久性地失去引用,最后变成死亡状态。只有当一个对象处于死亡状态时,系统才会真正 回收该对象所占有的资源。 强制垃圾回收 •强制系统垃圾回收有如下两个方法:   –调用System类的gc()静态方法:System.gc()   –调用Runtime对象的gc()实例方法:Runtime.getRuntime().gc() finalize方法 •finalize方法有如下四个特点:   –永远不要主动调用某个对象的finalize方法,该方法应交给垃圾回收机制调用。   –finalize方法的何时被调用,是否被调用具有不确定性。不要把finalize方法当成一定会被执行的方法。   –当JVM执行去活对象的finalize方法时,可能使该对象,或系统中其他对象重新变成激活状态。   –当JVM执行finalize方法时出现了异常,垃圾回收机制不会报告异常,程序继续执行。 对象的软、弱和虚引用 •强引用(StrongReference) •软引用-软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回 收。 •弱引用-弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引 用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。 •虚引用-虚引用通过PhantomReference类实现,虚应用完全类似于没有引用。虚引用对对象本身没有太大影 响,对象甚至感觉不到虚引用的存在。 修饰符的适用范围 顶层类/接口 成员属性 方法 构造器 初始化块 成员内部类 局部成员 public √ √ √ √ √ protected √ √ √ √ 包访问控制符 √ √ √ √ ○ √ ○ private √ √ √ √ abstract √ √ √ final √ √ √ √ √ static √ √ √ √ strictfp √ √ √ synchronized √ native √ transient √ volatile √ 使用JAR文件的好处 •1.安全 •2.快下载速度 •3.压缩 •4.包封装 •5.可移植性 jar命令详解 •-c 创建新文档,-t 列出存档内容的列表, -x 展开存档中的命名文件 •-u 更新已存在的存档,-v生成详细输出到标准输出上 •-f 指定存档文件名,-m 包含 来自标文件的标明信息 •-o 只存储方式:未用ZIP压缩格式 •-m 不产生所有项的清单文件,- I 为指定的jar文件产生索引信息 •-c 改变到指定的目录, 创建可执行的JAR包 •1.使用平台相关的编译器将整个应用编译成平台相关的可执行性文件 •2.为整个应用编辑一个批处理文件 关于JAR包的技巧 •相当于一个压缩文件。 •可使用WinRAR来压缩JAR包。 •也可使用WinRAR来查看JAR包。 现在贴出代码: AutoBoxingUnboxing Primitive2String UnsignedTest WrapperClassCompare EqualTest Person OverrideEqualsRight PrintObject StringCompareTest ToStringTest NullAccessStatic Singleton Address CacheImmutaleTest FinalErrorTest FinalLocalTest FinalLocalVariableTest FinalMethodTest FinalReferenceTest FinalReplaceTest FinalVariableTest ImmutableStringTest IntegerCacheTest Person Sub extends PrivateFinalMethodTest StringJoinTest CarSpeedMeter Circle extends Shape abstract class Shape SpeedMeter Triangle 复制代码 public class AddCommand implements Command { public void process(int[] target) { int sum = 0; for (int tmp : target) { sum += tmp; } System.out.println("数组元素的总和是:" + sum); } } **************************************************** public class BetterPrinter implements Output { private String[] printData = new String[MAX_CACHE_LINE * 2]; // 用以记录当前需打印的作业数 private int dataNum = 0; public void out() { // 只要还有作业,继续打印 while (dataNum > 0) { System.out.println("高速打印机正在打印:" + printData[0]); // 把作业队列整体前移一位,并将剩下的作业数减1 System.arraycopy(printData, 1, printData, 0, --dataNum); } } public void getData(String msg) { if (dataNum >= MAX_CACHE_LINE * 2) { System.out.println("输出队列已满,添失败"); } else { // 把打印数据添到队列里,已保存数据的数量1。 printData[dataNum++] = msg; } } } ************************************************ public interface Command { // 接口里定义的process()方法用于封装“处理行为” void process(int[] target); } ********************************************** public class CommandTest { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] target = { 3, -4, 6, 4 }; // 第一次处理数组,具体处理行为取决于PrintCommand pa.process(target, new PrintCommand()); System.out.println("------------------"); // 第二次处理数组,具体处理行为取决于AddCommand pa.process(target, new AddCommand()); } } ************************************************* public class Computer { private Output out; public Computer(Output out) { this.out = out; } // 定义一个模拟获取字符串输入的方法 public void keyIn(String msg) { out.getData(msg); } // 定义一个模拟打印的方法 public void print() { out.out(); } } ********************************************** interface interfaceA { int PROP_A = 5; void testA(); } interface interfaceB { int PROP_B = 6; void testB(); } interface interfaceC extends interfaceA, interfaceB { int PROP_C = 7; void testC(); } public class InterfaceExtendsTest { public static void main(String[] args) { System.out.println(interfaceC.PROP_A); System.out.println(interfaceC.PROP_B); System.out.println(interfaceC.PROP_C); } } ************************************************** public interface Output { // 接口里定义的成员变量只能是常量 int MAX_CACHE_LINE = 50; // 接口里定义的普通方法只能是public的抽象方法 void out(); void getData(String msg); // 在接口中定义默认方法,需要使用default修饰 default void print(String... msgs) { for (String msg : msgs) { System.out.println(msg); } } // 在接口中定义默认方法,需要使用default修饰 default void test() { System.out.println("默认的test()方法"); } // 在接口中定义类方法,需要使用static修饰 static String staticTest() { return "接口里的类方法"; } } ********************************************** public class OutputFactory { public Output getOutput() { // return new Printer(); return new BetterPrinter(); } public static void main(String[] args) { OutputFactory of = new OutputFactory(); Computer c = new Computer(of.getOutput()); c.keyIn("轻量级Java EE企业应用实战"); c.keyIn("疯狂Java讲义"); c.print(); } } *********************************************** public class OutputFieldTest { public static void main(String[] args) { // 访问另一个包中的Output接口的MAX_CACHE_LINE System.out.println(lee.Output.MAX_CACHE_LINE); // 下面语句将引起"为final变量赋值"的编译异常 // lee.Output.MAX_CACHE_LINE = 20; // 使用接口来调用类方法 System.out.println(lee.Output.staticTest()); } } ************************************************ public class PrintCommand implements Command { public void process(int[] target) { for (int tmp : target) { System.out.println("迭代输出目标数组的元素:" + tmp); } } } *********************************************** // 定义一个Product接口 interface Product { int getProduceTime(); } // 让Printer类实现Output和Product接口 public class Printer implements Output, Product { private String[] printData = new String[MAX_CACHE_LINE]; // 用以记录当前需打印的作业数 private int dataNum = 0; public void out() { // 只要还有作业,继续打印 while (dataNum > 0) { System.out.println("打印机打印:" + printData[0]); // 把作业队列整体前移一位,并将剩下的作业数减1 System.arraycopy(printData, 1, printData, 0, --dataNum); } } public void getData(String msg) { if (dataNum >= MAX_CACHE_LINE) { System.out.println("输出队列已满,添失败"); } else { // 把打印数据添到队列里,已保存数据的数量1。 printData[dataNum++] = msg; } } public int getProduceTime() { return 45; } public static void main(String[] args) { // 创建一个Printer对象,当成Output使用 Output o = new Printer(); o.getData("轻量级Java EE企业应用实战"); o.getData("疯狂Java讲义"); o.out(); o.getData("疯狂Android讲义"); o.getData("疯狂Ajax讲义"); o.out(); // 调用Output接口中定义的默认方法 o.print("孙悟空", "猪八戒", "白骨精"); o.test(); // 创建一个Printer对象,当成Product使用 Product p = new Printer(); System.out.println(p.getProduceTime()); // 所有接口类型的引用变量都可直接赋给Object类型的变量 Object obj = p; } } ************************************************* public class ProcessArray { public void process(int[] target, Command cmd) { cmd.process(target); } } 复制代码 。。。。。。。。。。。。。。。。
1.面向对象的概念 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程 架构,OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成,OOP 达到了软件工程的三个目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收 信息、处理数据和向其它对象发送信息。面向对象一直是软件开发领域内比较热门的话题,首先, 面向对象符合人类看待事物的一般规律。其次,采用面向对象方法可以使系统各部分各司其职、各 尽所能。为编程人员敞开了一扇大门,使其编程的代码更简洁、更易于维护,并且具有更强的可重 用性。有人说PHP 不是一个真正的面向对象的语言,这是事实。PHP 是一个混合型语言,你可以使 用OOP,也可以使用传统的过程化编程。然而,对于大型项目,你可能需要在PHP 中使用纯的OOP 去声明类,而且在你的项目里只用对象和类。这个概念我先不多说了,因为有很多朋友远离面向对 象编程的主要原因就是一接触面向对象概念的时候就理解不上去, 所以就不想去学下去了。等读 者看完整篇内容后再去把概念搞明白吧。 2.什么是类,什么是对象,类和对象之间的关系 类的概念:类是具有相同属性和服务的一组对象的集合。它为属于该类的所有对象提供了统一 的抽象描述,其内部包括属性和服务两个主要部分。在面向对象的编程语言中,类是一个独立的程 序单位,它应该有一个类名并包括属性说明和服务说明两个主要部分。 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》66/104 对象的概念:对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一 个对象由一组属性和对这组属性进行操作的一组服务组成。从更抽象的角度来说,对象是问题域或 实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属 性和有权对这些属性进行操作的一组服务的封装体。客观世界是由对象对象之间的联系组成的。 类与对象的关系就如模具和铸件的关系,类的实例化结果就是对象,而对一类对象的抽象就是 类。类描述了一组有相同特性(属性)和相同行为(方法)的对象。 上面大概就是它们的定义吧,也许你是刚接触面向对象的朋友, 不要被概念的东西搞晕了,给 你举个例子吧,如果你去中关村想买几台组装的PC 机,到了那里你第一步要干什么,是不是装机 的工程师和你坐在一起,按你提供的信息和你一起完成一个装机的配置单呀,这个配置单就可以想 象成是类,它就是一张纸,但是它上面记录了你要买的PC 机的信息,如果用这个配置单买10 台机 器,那么这10 台机子,都是按这个配置单组成的,所以说这10 台机子是一个类型的,也可以说是 一类的。那么什么是对象呢,类的实例化结果就是对象,用这个配置单配置出来(实例化出来)的 机子就是对象,是我们可以操作的实体,10 台机子,10 个对象。每台机子都是独立的,只能说明 他们是同一类的,对其中一个机做任何动作都不会影响其它9 台机器,但是我对类修改,也就是在 这个配置单上一个或少一个配件,那么装出来的9 个机子都改变了,这是类和对象的关系(类的 实例化结果就是对象)。 3.什么是面向对象编程呢? 就不说他的概念,如果你想建立一个电脑教室,首先要有一个房间, 房间里面要有N 台电脑, 有N 张桌子, N 把椅子, 白板, 投影机等等,这些是什么,刚才咱们说了,这就是对象,能看 到的一个个的实体,可以说这个电脑教室的单位就是这一个个的实体对象, 它们共同组成了这个 电脑教室,那么我们是做程序,这和面向对象有什么关系呢?开发一个系统程序和建一个电脑教室 类似,你把每个独立的功能模块抽象成类,形成对象,由多个对象组成这个系统,这些对象之间都 能够接收信息、处理数据和向其它对象发送信息等等相互作用。就构成了面向对象的程序。 4.如何抽象出一个类? 上面已经介绍过了,面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,所以 我们首先要做的就是如何来声明类,做出来一个类很容易,只要掌握基本的程序语法定义规则就可 以做的出来,那么难点在那里呢?一个项目要用到多少个类,用多少个对象,在那要定义类,定义 一个什么样的类,这个类实例化出多少个对象,类里面有多少个属性,有多少个方法等等,这就需 要读者通过在实际的开发中就实际问题分析设计和总结了。 类的定义: class 类名{ } 使用一个关键字class 和后面上一个你想要的类名以及上一对大括号, 这样一个类的结构 就定义出来了,只要在里面写代码就可以了,但是里面写什么?能写什么?怎样写才是一个完整的 类呢?上面讲过来,使用类是为了让它实例出对象来给我们用,这就要知道你想要的是什么样的对 象了,像上面我们讲的一个装机配置单上写什么,你装出来的机子就有什么。比如说,一个人就是 一个对象,你怎么把一个你看好的人推荐给你们领导呢?当然是越详细越好了: 首先,你会介绍这个人姓名、性别、年龄、身高、体重、电话、家庭住址等等。 然后,你要介绍这个人能做什么,可以开车,会说英语,可以使用电脑等等。 只要你介绍多一点,别人对这个人就多一点了解,这就是我们对一个人的描述, 现在我们总结 一下,所有的对象我们用类去描述都是类似的,从上面人的描述可以看到, 做出一个类来,从定 义的角度分两部分,第一是从静态上描述,第二是从动态上描述, 静态上的描述就是我们所说的 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》67/104 属性,像上面我们看到的,人的姓名、性别、年龄、身高、体重、电话、家庭住址等等。动态上也 就是人的这个对象的功能,比如这个人可以开车,会说英语,可以使用电脑等等,抽象成程序时, 我们把动态的写成函数或者说是方法,函数和方法是一样的。所以,所有类都是从属性和方法这两 方面去写, 属性又叫做这个类的成员属性,方法叫做这个类的成员方法。 class 人{ 成员属性:姓名、性别、年龄、身高、体重、电话、家庭住址 成员方法:可以开车, 会说英语, 可以使用电脑 } 属性: 通过在类定义中使用关键字" var "来声明变量,即创建了类的属性,虽然在声明成员属性 的时候可以给定初始值, 但是在声明类的时候给成员属性初始值是没有必要的,比如说要 是把人的姓名赋上“张三”,那么用这个类实例出几十个人,这几十个人都叫张三了,所以 没有必要, 我们在实例出对象后给成员属性初始值就可以了。 如: var $somevar; 方法(成员函数): 通过在类定义中声明函数,即创建了类的方法。 如: function somefun(参数列表) { ... ... } <?php class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //下面是人的成员方法 function say() //这个人可以说话的方法 { echo "这个人在说话"; }f unction run() //这个人可以走路的方法 { echo "这个人在走路"; } } ?> 上面就是一个类的声明,从属性和方法上声明出来的一个类,但是成员属性最好在声明的时候 不要给初始的值,因为我们做的人这个类是一个描述信息,将来用它实例化对象,比如实例化出来 10 个人对象,那么这10 个人, 每一个人的名字、性别、年龄都是不一样的,所以最好不要在这个 地方给成员属性赋初值,而是对每个对象分别赋值的。 用同样的办法可以做出你想要的类了,只要你能用属性和方法能描述出来的实体都可以定义成 类,去实例化对象。 为了强你对类的理解,我们再做一个类,做一个形状的类,形状的范围广了点, 我们就做个 矩形吧,先分析一下,想一想从两方面分析,矩形的属性都有什么?矩形的功能都有什么? class 矩形 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》68/104 { //矩形的属性 矩形的长; 矩形的宽; //矩形的方法 矩形的周长; 矩形的面积; } <?php class Rect { var $kuan; var $gao; function zhouChang() { 计算矩形的周长; }f unction mianJi() { 计算矩形的面积; } } ?> 如果用这个类来创建出多个矩形对象,每个矩形对象都有自己的长和宽, 都可以求出自己的周 长和面积了。 类的声明我们就到这里吧!! 5.如何实例化对象 我们上面说过面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,既然我们类 会声明了,下一步就是实例化对象了。 当定义好类后,我们使用new 关键字来生成一个对象。 $对象名称= new 类名称(); <?php class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //下面是人的成员方法 function say() //这个人可以说话的方法 { echo "这个人在说话"; }f unction run() //这个人可以走路的方法 { echo "这个人在走路"; } } $p1=new Person(); $p2=new Person(); LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》69/104 $p3=new Person(); ?> $p1=new Person(); 这条代码就是通过类产生实例对象的过程,$p1 就是我们实例出来的对象名称,同理,$p2, $p3 也是我们实例出来的对象名称,一个类可以实例出多个对象,每个对象都是独立的,上面的代码相 当于实例出来3 个人来,每个人之间是没有联系的,只能说明他们都是人类,每个人都有自己的姓 名,性别和年龄的属性,每个人都有说话和走路的方法,只要是类里面体现出来的成员属性和成员 方法,实例化出来的对象里面就包含了这些属性和方法。 对像在PHP 里面和整型、浮点型一样,也是一种数据类,都是存储不同类型数据用的,在运行 的时候都要载到内存中去用, 那么对象在内存里面是怎么体现的呢?内存从逻辑上说大体上是 分为4 段,栈空间段、堆空间段、代码段、初始化静态段,程序里面不同的声明放在不同的内存段 里面,栈空间段是存储占用相同空间长度并且占用空间小的数据类型的地方,比如说整型1,10, 100,1000,10000,100000 等等,在内存里面占用空间是等长的,都是64 位4 个字节。那么数据 长度不定长,而且占有空间很大的数据类型的数据放在那内存的那个段里面呢?这样的数据是放在 堆内存里面的。栈内存是可以直接存取的,而堆内存是不可以直接存取的内存。对于我们的对象来 数就是一种大的数据类型而且是占用空间不定长的类型,所以说对象是放在堆里面的,但对象名称 是放在栈里面的,这样通过对象名称就可以使用对象了。 $p1=new Person(); 对于这个条代码, $p1 是对象名称在栈内存里面,new Person()是真正的对象是在堆内存里面 的,具体的请看下图: 从上图可以看出$p1=new Person();等号右边是真正的对象实例,在堆内存里面的实体,上图一 共有3 次new Person(),所以会在堆里面开辟3 个空间,产生3 个实例对象,每个对象之间都是相 互独立的,使用自己的空间,在PHP 里面,只要有一个new 这个关键字出现就会实例化出来一个对 象,在堆里面开辟一块自己的空间。 每个在堆里面的实例对象是存储属性的,比如说,现在堆里面的实例对象里面都存有姓名、性 别和年龄。每个属性又都有一个地址。 $p1=new Person();等号的右边$p1 是一个引用变量,通过赋值运算符“=”把对象的首地址赋 给“$p1”这个引用变量,所以$p1 是存储对象首地址的变量,$p1 放在栈内存里边,$p1 相当于一 个指针指向堆里面的对象,所以我们可以通过$p1 这个引用变量来操作对象,通常我们也称对象引用 为对象。 6.如何去使用对象中的成员 上面看到PHP 对象中的成员有两种一种是成员属性,一种是成员方法。对象我们以经可以声明 了,$p1=new Person();怎么去使用对象的成员呢?要想访问对象中的成员就要使用一个特殊的操 作符“->”来完成对象成员的访问: LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》70/104 对象->属性$p1->name; $p2->age; $p3->sex; 对象->方法$p1->say(); $p2->run(); 如下面实例: <?php class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //下面是人的成员方法 function say() //这个人可以说话的方法 { echo "这个人在说话"; }f unction run() //这个人可以走路的方法 { echo "这个人在走路"; } } $p1=new Person(); //创建实例对象$p1 $p2=new Person(); //创建实例对象$p2 $p3=new Person(); //创建实例对象$p3 //下面三行是给$p1对象属性赋值 $p1->name=”张三”; $p1->sex=”男”; $p1->age=20; //下面三行是访问$p1对象的属性 echo “p1对象的名字是:”.$p1->name.”<br>”; echo “p1对象的性别是:”.$p1->sex.”<br>”; echo “p1对象的年龄是:”.$p1->age.”<br>”; //下面两行访问$p1对象中的方法 $p1->say(); $p1->run(); //下面三行是给$p2对象属性赋值 $p2->name=”李四”; $p2->sex=”女”; $p2->age=30; //下面三行是访问$p2对象的属性 echo “p2对象的名字是:”.$p2->name.”<br>”; echo “p2对象的性别是:”.$p2->sex.”<br>”; echo “p2对象的年龄是:”.$p2->age.”<br>”; //下面两行访问$p2对象中的方法 $p2->say(); $p2->run(); //下面三行是给$p3对象属性赋值 $p3->name=”王五”; $p3->sex=”男”; $p3->age=40; //下面三行是访问$p3对象的属性 echo “p3对象的名字是:”.$p3->name.”<br>”; echo “p3对象的性别是:”.$p3->sex.”<br>”; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》71/104 echo “p3对象的年龄是:”.$p3->age.”<br>”; //下面两行访问$p3对象中的方法 $p3->say(); $p3->run(); ?> 从上例中可以看出只是对象里面的成员就要使用对象->属性、对象->方法形式访问,再没有第 二种方法来访问对象中的成员了。 7.特殊的引用“$this”的使用 现在我们知道了如何访问对象中的成员,是通过“对象->成员”的方式访问的,这是在对象的外 部去访问对象中成员的形式,那么如果我想在对象的内部,让对象里的方法访问本对象的属性,或 是对象中的方法去调用本对象的其它方法这时我们怎么办?因为对象里面的所有的成员都要用对 象来调用,包括对象的内部成员之间的调用,所以在PHP 里面给我提供了一个本对象的引用$this, 每个对象里面都有一个对象的引用$this 来代表这个对象,完成对象内部成员的调用, this 的本意就是 “这个”的意思,上面的实例里面,我们实例化三个实例对象$P1、$P2、$P3,这三个对象里面各 自存在一个$this 分别代表对象$p1、$p2、$p3 。 通过上图我们可以看到,$this 就是对象内部代表这个对象的引用,在对象内部和调用本对象的 成员和对象外部调用对象的成员所使用的方式是一样的。 $this->属性$this->name; $this->age; $this->sex; $this->方法$this->say(); $this->run(); 修改一下上面的实例,让每个人都说出自己的名字,性别和年龄: <?php class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //下面是人的成员方法 function say() //这个人可以说话的方法 { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; }f unction run() //这个人可以走路的方法 { echo "这个人在走路"; } } $p1=new Person(); //创建实例对象$p1 $p2=new Person(); //创建实例对象$p2 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》72/104 $p3=new Person(); //创建实例对象$p3 //下面三行是给$p1对象属性赋值 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //下面访问$p1对象中的说话方法 $p1->say(); //下面三行是给$p2对象属性赋值 $p2->name="李四"; $p2->sex="女"; $p2->age=30; //下面访问$p2对象中的说话方法 $p2->say(); //下面三行是给$p3对象属性赋值 $p3->name="王五"; $p3->sex="男"; $p3->age=40; //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 分析一下这个方法: function say() //这个人可以说话的方法 { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; } 在$p1、$p2 和$p3 这三个对象中都有say()这个方法,$this 分别代表这三个对象, 调用相应 的属性,打印出属性的值,这就是在对象内部访问对象属性的方式, 如果相在say()这个方法里调 用run()这个方法也是可以的,在say()这个方法中使用$this->run()的方式来完成调用。 8.构造方法与析构方法 大多数类都有一种称为构造函数的特殊方法。当创建一个对象时,它将自动调用构造函数,也 就是使用new 这个关键字来实例化对象的时候自动调用构造方法。 构造函数声明与其它操作的声明一样,只是其名称必须是__construct( )。这是PHP5 中的变化, 以前的版本中,构造函数的名称必须与类名相同,这种在PHP5 中仍然可以用,但现在以经很少有 人用了,这样做的好处是可以使构造函数独立于类名,当类名发生改变时不需要改相应的构造函数 名称了。为了向下兼容,如果一个类中没有名为__construct( )的方法,PHP 将搜索一个php4 中的写 法,与类名相同名的构造方法。 格式:function __construct ( [参数] ) { ... ... } 在一个类中只能声明一个构造方法,而是只有在每次创建对象的时候都会去调用一次构造方法, 不能主动的调用这个方法,所以通常用它执行一些有用的初始化任务。比如对成属性在创建对象的 时候赋初值。 <? //创建一个人类 class Person { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》73/104 //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //定义一个构造方法参数为姓名$name、性别$sex和年龄$age function __construct($name, $sex, $age) { //通过构造方法传进来的$name给成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给成员属性$this->age赋初使值 $this->age=$age; }/ /这个人的说话方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; } } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 如图: LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》74/104 析构函数: 与构造函数相对的就是析构函数。析构函数是PHP5 新添的内容,在PHP4 中没有析构函数。 析构函数允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件,释放结果集等, 析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,也就是对象在内存中 被销毁前调用析构函数。与构造函数的名称类似,一个类的析构函数名称必须是__destruct( )。析构 函数不能带有任何参数。 格式:function __destruct ( ) { ... ... } <? //创建一个人类 class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //定义一个构造方法参数为姓名$name、性别$sex和年龄$age function __construct($name, $sex, $age) { //通过构造方法传进来的$name给成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给成员属性$this->age赋初使值 $this->age=$age; } //这个人的说话方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; } //这是一个析构函数,在对象销毁前调用 function __destruct() { echo “再见”.$this->name.”<br>”; } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》75/104 再见张三 再见李四 再见王五 9.封装性 封装性是面向对象编程中的三大特性之一,封装性就是把对象的属性和服务结合成一个独立的 相同单位,并尽可能隐蔽对象的内部细节,包含两个含义:1.把对象的全部属性和全部服务结合在一 起,形成一个不可分割的独立单位(即对象)。2.信息隐蔽,即尽可能隐蔽对象的内部细节,对外形 成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。 封装的原则在软件上的反映是:要求使对象以外的部分不能随意存取对象的内部数据(属性), 从而有效的避免了外部错误对它的"交叉感染",使软件错误能够局部化,大大减少查错和排错的难 度。 用个实例来说明吧,假如某个人的对象中有年龄和工资等属性,像这样个人隐私的属性是不想 让其它人随意就能获得到的,如果你不使用封装,那么别人想知道就能得到,但是如果你封装上之 后别人就没有办法获得封装的属性,除非你自己把它说出去,否则别人没有办法得到。 再比如说,个人电脑都有一个密码,不想让其它人随意的登陆,在你的电脑里面拷贝和粘贴。 还有就是像人这个对象,身高和年龄的属性,只能是自己来增长,不可以让别人随意的赋值等等。 使用private 这个关键字来对属性和方法进行封装: 原来的成员: var $name; //声明人的姓名 var $sex; //声明人的性别 var $age; //声明人的年龄 function run(){… … .} 改成封装的形式: private $name; //把人的姓名使用private 关键字进行封装 private $sex; //把人的性别使用private 关键字进行封装 private $age; //把人的年龄使用private 关键字进行封装 private function run(){… … } //把人的走路方法使用private 关键字进行封装 注意:只要是成员属性前面有其它的关键字就要去掉原有的关键字“var”。 通过private 就可以把人的成员(成员属性和成员方法)封装上了。封装上的成员就不能被类外 面直接访问了,只有对象内部自己可以访问;下面的代码会产生错误: class Person { //下面是人的成员属性 private $name; //人的名字,被private封装上了 private $sex; //人的性别, 被private封装上了 private $age; //人的年龄, 被private封装上了 //这个人可以说话的方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; }/ /这个人可以走路的方法, 被private封装上了 private function run() { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》76/104 echo "这个人在走路"; } } //实例化一个人的实例对象 $p1=new Person(); //试图去给私有的属性赋值, 结果会发生错误 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //试图去打印私有的属性, 结果会发生错误 echo $p1->name.”<br>”; echo $p1->sex.”<br>”; echo $p1->age.”<br>” //试图去打印私有的成员方法, 结果会发生错误 $p1->run(); 输出结果为: Fatal error: Cannot access private property Person::$name Fatal error: Cannot access private property Person::$sex Fatal error: Cannot access private property Person::$age Fatal error: Cannot access private property Person::$name Fatal error: Call to private method Person::run() from context '' 从上面的实例可以看到,私有的成员是不能被外部访问的,因为私有成员只能在本对象内部自 己访问,比如,$p1 这个对象自己想把他的私有属性说出去,在say()这个方法里面访问了私有属性, 这样是可以。(没有任何访问控制,默认的是public 的,任何地方都可以访问) //这个人可以说话的方法, 说出自己的私有属性,在这里也可以访问私有方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; //在这里也可以访问私有方法 //$this->run(); } 因为成员方法say()是公有的, 所以我们在类的外部调用say()方法是可以的,改变上面的代码; class Person { //下面是人的成员属性 private $name; //人的名字,被private封装上了 private $sex; //人的性别, 被private封装上了 private $age; //人的年龄, 被private封装上了 //定义一个构造方法参数为私有的属性姓名$name、性别$sex和年龄$age进行赋值 function __construct($name, $sex, $age) { //通过构造方法传进来的$name给私有成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给私有成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给私有成员属性$this->age赋初使值 $this->age=$age; }/ /这个人可以说话的方法, 说出自己的私有属性,在这里也可以访问私有方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."<br>"; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》77/104 } } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 因为构造方法是默认的公有方法(构造方法不要设置成私有的),所以在类的外面可以访问到, 这样就可以使用构造方法创建对象, 另外构造方法也是类里面的函数,所以可以用构造方法给私 有的属性赋初值。Say()的方法是默认公有的, 所以在外面也可以访问的到, 说出他自己的私有属 性。 从上面的例子中我们可以看到,私有的成员只能在类的内部使用,不能被类外部直接来存取, 但是在类的内部是有权限访问的,所以有时候我们需要在类的外面给私有属性赋值和读取出来,也 就是给类的外部提供一些可以存取的接口,上例中构造方法就是一种赋值的形式,但是构造方法只 是在创建对象的时候赋值,如果我们已经有一个存在的对象了,想对这个存在的对象赋值,这个时 候,如果你还使用构造方法传值的形式传值,那么就创建了一个新的对象,并不是这个已存在的对 象了。所以我们要对私有的属性做一些可以被外部存取的接口,目的就是可以在对象存在的情况下, 改变和存取属性的值,但要注意,只有需要让外部改变的属性才这样做,不想让外面访问的属性是 不做这样的接口的,这样就能达到封装的目的,所有的功能都是对象自己来完成,给外面提供尽量 少的操作。 如果给类外部提供接口,可以为私有属性在类外部提供设置方法和获取方法,来操作私有属性. 例如: prvate $age; //私有的属性年龄 function setAge($age) //为外部提供一个公有设置年龄的方法 { if($age<0 || $age>130) //在给属性赋值的时候,为了避免非法值设置给属性 return; $this->age=$age; }f unction getAge() //为外部提供一个公有获取年龄的方法 { return($this->age); } 上面的方法是为一个成员属性设置和获取值, 当然你也可以为每个属性用同样的方法对其进行 赋值和取值的操作,完成在类外部的存取工作。 10.__set() __get() __isset() __unset()四个方法的应用 一般来说,总是把类的属性定义为private,这更符合现实的逻辑。但是,对属性的读取和赋值 操作是非常频繁的,因此在PHP5 中,预定义了两个函数“__get()”和“__set()”来获取和赋值其 属性,以及检查属性的“__isset()”和删除属性的方法“__unset()”。 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》78/104 上一节中,我们为每个属性做了设置和获取的方法,在PHP5 中给我们提供了专门为属性设置 值和获取值的方法,“__set()”和“__get()”这两个方法,这两个方法不是默认存在的,而是我们手 工添到类里面去的,像构造方法(__construct())一样, 类里面添了才会存在,可以按下面的方式 来添这两个方法,当然也可以按个人的风格来添: //__get()方法用来获取私有属性 private function __get($property_name) { if(isset($this->$property_name)) { return($this->$property_name); }else { return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { $this->$property_name = $value; } __get()方法:这个方法用来获取私有成员属性值的,有一个参数,参数传入你要获取的成员属性 的名称,返回获取的属性值,这个方法不用我们手工的去调用,因为我们也可以把这个方法做成私 有的方法,是在直接获取私有属性的时候对象自动调用的。因为私有属性已经被封装上了,是不能 直接获取值的(比如:“echo $p1->name”这样直接获取是错误的),但是如果你在类里面上了这 个方法,在使用“echo $p1->name”这样的语句直接获取值的时候就会自动调用__get($property_name) 方法,将属性name 传给参数$property_name,通过这个方法的内部执行,返回我们传入的私有属性 的值。如果成员属性不封装成私有的,对象本身就不会去自动调用这个方法。 __set()方法:这个方法用来为私有成员属性设置值的,有两个参数,第一个参数为你要为设置 值的属性名,第二个参数是要给属性设置的值,没有返回值。这个方法同样不用我们手工去调用, 它也可以做成私有的,是在直接设置私有属性值的时候自动调用的,同样属性私有的已经被封装上 了, 如果没有__set()这个方法,是不允许的, 比如:$this->name=‘zhangsan’, 这样会出错,但 是如果你在类里面上了__set($property_name, $value)这个方法,在直接给私有属性赋值的时候, 就会自动调用它,把属性比如name 传给$property_name, 把要赋的值“zhangsan”传给$value,通过 这个方法的执行,达到赋值的目的。如果成员属性不封装成私有的,对象本身就不会去自动调用这 个方法。为了不传入非法的值, 还可以在这个方法给做一下判断。代码如下: <?php class Person { //下面是人的成员属性, 都是封装的私有成员 private $name; //人的名字 private $sex; //人的性别 private $age; //人的年龄 //__get()方法用来获取私有属性 private function __get($property_name) { echo "在直接获取私有属性值的时候,自动调用了这个__get()方法<br>"; if(isset($this->$property_name)) { return($this->$property_name); }e lse { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》79/104 return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值<br>"; $this->$property_name = $value; } } $p1=new Person(); //直接为私有属性赋值的操作, 会自动调用__set()方法进行赋值 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //直接获取私有属性的值, 会自动调用__get()方法,返回成员属性的值 echo "姓名:".$p1->name."<br>"; echo "性别:".$p1->sex."<br>"; echo "年龄:".$p1->age."<br>"; ?> 程序执行结果: 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接获取私有属性值的时候,自动调用了这个__get()方法 姓名:张三 在直接获取私有属性值的时候,自动调用了这个__get()方法 性别:男 在直接获取私有属性值的时候,自动调用了这个__get()方法 年龄:20 以上代码如果不上__get()和__set()方法,程序就会出错,因为不能在类的外部操作私有成员, 而上面的代码是通过自动调用__get()和__set()方法来帮助我们直接存取封装的私有成员的。 __isset() 方法:在看这个方法之前我们看一下“isset()”函数的应用,isset()是测定变量是否设 定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false。那么如果 在一个对象外面使用“isset()”这个函数去测定对象里面的成员是否被设定可不可以用它呢?分两种 情况,如果对象里面成员是公有的,我们就可以使用这个函数来测定成员属性,如果是私有的成员 属性,这个函数就不起作用了,原因就是因为私有的被封装了,在外部不可见。那么我们就不可以 在对象的外部使用“isset()”函数来测定私有成员属性是否被设定了呢?可以,你只要在类里面上 一个“__isset()”方法就可以了,当在类外部使用”isset()”函数来测定对象里面的私有成员是否被设 定时, 就会自动调用类里面的“__isset()”方法了帮我们完成这样的操作,“__isset()”方法也可以 做成私有的。你可以在类里面上下面这样的代码就可以了: private function __isset($nm) { echo "当在类外部使用isset()函数测定私有成员$nm时,自动调用<br>"; return isset($this->$nm); } __unset()方法:看这个方法之前呢,我们也先来看一下“unset()”这个函数,“unset()”这个函 数的作用是删除指定的变量且传回true,参数为要删除的变量。那么如果在一个对象外部去删除对 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》80/104 象内部的成员属性用“unset()”函数可不可以呢,也是分两种情况,如果一个对象里面的成员属性 是公有的,就可以使用这个函数在对象外面删除对象的公有属性,如果对象的成员属性是私有的, 我使用这个函数就没有权限去删除,但同样如果你在一个对象里面上“__unset()”这个方法,就 可以在对象的外部去删除对象的私有成员属性了。在对象里面上了“__unset()”这个方法之后, 在对象外部使用“unset()”函数删除对象内部的私有成员属性时,自动调用“__unset()”函数来帮 我们删除对象内部的私有成员属性,这个方法也可以在类的内部定义成私有的。在对象里面上下 面的代码就可以了: private function __unset($nm) { echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>"; unset($this->$nm); } 我们来看一个完整的实例: <?php class Person { //下面是人的成员属性 private $name; //人的名字 private $sex; //人的性别 private $age; //人的年龄 //__get()方法用来获取私有属性 private function __get($property_name) { if(isset($this->$property_name)) { return($this->$property_name); }else { return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { $this->$property_name = $value; }/ /__isset()方法 private function __isset($nm) { echo "isset()函数测定私有成员时,自动调用<br>"; return isset($this->$nm); }/ /__unset()方法 private function __unset($nm) { echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>"; unset($this->$nm); } } $p1=new Person(); $p1->name="this is a person name"; //在使用isset()函数测定私有成员时,自动调用__isset()方法帮我们完成,返回结果为true echo var_dump(isset($p1->name))."<br>"; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:[email protected] 《PHPer》81/104 echo $p1->name."<br>"; //在使用unset()函数删除私有成员时,自动调用__unset()方法帮我们完成,删除name私有属性 unset($p1->name); //已经被删除了, 所这行不会有输出 echo $p1->name; ?> 输出结果为: isset()函数测定私有成员时,自动调用 bool(true) this is a person name 当在类外部使用unset()函数来删除私有成员时自动调用的 __set()、__get()、__isset()、__unset() 这四个方法都是我们添对象里面的,在需要时自动调 用的,来完成在对象外部对对象内部私有属性的操作。
C++ Primer中文版(第5版)[203M]分3个压缩包 本书是久负盛名的C++经典教程,其内容是C++大师Stanley B. Lippman丰富的实践经验和C++标准委员会原负责人Josée Lajoie对C++标准深入理解的完美结合,已经帮助全球无数程序员学会了C++。 对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使本书成为C++初学者的最佳指南;对于中高级程序员,本书也是不可或缺的参考书。 目录 第1章 开始 1   1.1 编写一个简单的C++程序 2   1.1.1 编译、运行程序 3   1.2 初识输入输出 5   1.3 注释简介 8   1.4 控制流 10   1.4.1 while语句 10   1.4.2 for语句 11   1.4.3 读取数量不定的输入数据 13   1.4.4 if语句 15   1.5 类简介 17   1.5.1 Sales_item类 17   1.5.2 初识成员函数 20   1.6 书店程序 21   小结 23   术语表 23   第Ⅰ部分 C++基础 27   第2章 变量和基本类型 29   2.1 基本内置类型 30   2.1.1 算术类型 30   2.1.2 类型转换 32   2.1.3 字面值常量 35   2.2 变量 38   2.2.1 变量定义 38   2.2.2 变量声明和定义的关系 41   2.2.3 标识符 42   2.2.4 名字的作用域 43   2.3 复合类型 45   2.3.1 引用 45   2.3.2 指针 47   2.3.3 理解复合类型的声明 51   2.4 const限定符 53   2.4.1 const的引用 54   2.4.2 指针和const 56   2.4.3 顶层const 57   2.4.4 constexpr和常量表达式 58   2.5 处理类型 60   2.5.1 类型别名 60   2.5.2 auto类型说明符 61   2.5.3 decltype类型指示符 62   2.6 自定义数据结构 64   2.6.1 定义Sales_data类型 64   2.6.2 使用Sales_data类 66   2.6.3 编写自己的头文件 67   小结 69   术语表 69   第3章 字符串、向量和数组 73   3.1 命名空间的using声明 74   3.2 标准库类型string 75   3.2.1 定义和初始化string对象 76   3.2.2 string对象上的操作 77   3.2.3 处理string对象中的字符 81   3.3 标准库类型vector 86   3.3.1 定义和初始化vector对象 87   3.3.2 向vector对象中添元素 90   3.3.3 其他vector操作 91   3.4 迭代器介绍 95   3.4.1 使用迭代器 95   3.4.2 迭代器运算 99   3.5 数组 101   3.5.1 定义和初始化内置数组 101   3.5.2 访问数组元素 103   3.5.3 指针和数组 105   3.5.4 C风格字符串 109   3.5.5 与旧代码的接口 111   3.6 多维数组 112   小结 117   术语表 117   第4章 表达式 119   4.1 基础 120   4.1.1 基本概念 120   4.1.2 优先级与结合律 121   4.1.3 求值顺序 123   4.2 算术运算符 124   4.3 逻辑和关系运算符 126   4.4 赋值运算符 129   4.5 递增和递减运算符 131   4.6 成员访问运算符 133   4.7 条件运算符 134   4.8 位运算符 135   4.9 sizeof运算符 139   4.10 逗号运算符 140   4.11 类型转换 141   4.11.1 算术转换 142   4.11.2 其他隐式类型转换 143   4.11.3 显式转换 144   4.12 运算符优先级表 147   小结 149   术语表 149   第5章 语句 153   5.1 简单语句 154   5.2 语句作用域 155   5.3 条件语句 156   5.3.1 if语句 156   5.3.2 switch语句 159   5.4 迭代语句 165   5.4.1 while语句 165   5.4.2 传统的for语句 166   5.4.3 范围for语句 168   5.4.4 do while语句 169   5.5 跳转语句 170   5.5.1 break

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值