面向对象 -教案
面向对象思想:
万物皆对象
面向对象三大特性:
封装:
封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就 是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
继承:
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类 为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。
定义
1
2
3
4
|
class <派生类名>:<继承方式><基类名> { <派生类新定义成员> }; |
1
2
3
4
5
6
7
|
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,… { <派生类新定义成员> }; |
1
2
3
4
5
|
class Base1{}; struct Base2{}; class Derive:Base1,Base2{}; |
1
|
class Derive: private Base1, public Base2{}; |
public
|
protected
|
private
|
|
公有继承
|
public
|
protected
|
不可见
|
私有继承
|
private
|
private
|
不可见
|
保护继承
|
protected
|
protected
|
不可见
|
公有方式
私有方式
保护方式
具体化
延续化
派生类
控制方式
重定义
多态:
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数实现的。
classA { public: A(){} virtual void foo() { cout<<"ThisisA."<<endl; } }; classB:publicA { public: B(){} void foo() { cout<<"ThisisB."<<endl; } }; int main(intargc,char*argv[]) { A* a = new B(); a->foo(); if(a != NULL) delete a; return0; }
public interface Parent//父类接口 { public void simpleCall(); } public class Child_A implements Parent { public void simpleCall(); { //具体的实现细节; } } public class Child_B implements Parent { public void simpleCall(); { //具体的实现细节; } }
类Class:
-------------------------------------------
封装:
成员变量:
package ch05.sample;
public class Clothes
{
String id; //实例变量
private String colorType; //实例变量
private int size; //实例变量
private static String depart; //类变量
final String design="yangzi"; //常量
}
|
修饰符
|
说明
|
public
|
成员变量可以被项目中的任何方法访问,建议尽量少用
|
protected
|
不在同一个包中的类不能访问,但子类可以访问
|
private
|
只能在同一个类中使用
|
static
|
类变量,其值为该类的所有对象共享,不会因类的对象不同而不同
|
final
|
最终成员变量,其值保持不变,即常量
|
transient
|
当对象被持久化时(例如写入数据库),该成员变量的值不需要保存
|
volatile
|
同步多线程访问的成员变量的值,以便使不同的线程总是得到
该成员变量的同一个值。关于线程,请参阅第11章
|
属性:
c# 属性
属性:get { //读属性代码 } set { //写属性代码 }
public class Person
{
private string name;
public string Name
{
get{return name;}
set{ name=value;}
}
}
属性可以忽略get或set访问器,但是不能两个都忽略.
set访问器包含一个隐藏的参数value,该参数包含从客户代码传送过来的值.
公共属性及其底层类型最好使用相同的名称,因为它们之间的联系将很清晰.
字段使用camelCase(xxXxx),如dateOfBirth,而属性使用PacalCase(XxXxx),如DateOfBirth.一些开发人员喜欢在字段的开头使用下划线,如_Name,属性也应使用名词来命名.
c#通过属性特性读取和写入字段,而不直接读取和写入,以此来提供对类中字段的保护.
属性按可以访问的类型分为三种不同的类型:
一.读/写属性
读/写属性是一个具有get()和set()访问器的属性.
语法: [访问修饰符] 数据类型 属性
{
get{ };
set{ };
}
二.只读属性
仅具有get()访问器属性称为只读属性.
语法: [访问修饰符] 数据类型 属性名
{
get{ };
}
三.只写属性
仅具有set()访问器属性称为只写属性,不推荐使用只写属性.
语法: [访问修饰符] 数据类型 属性名
{
set{ };
}
示例:
using System;
namespace Example1
{
class Student
{
#region/***属性***/
/// <summary>
/// 姓名
/// </summary>
private string name;
public string Name
{
get
{
return name;
}
set
{
if(value.length<40)
{
Console.WriteLine("学生姓名长度不能小于4个!");
return;
}
name=value;
}
}
#region
static void Main(string[ ] args)
{
Student student=new Student();
student.Name=Console.ReadLine();
}
}
}
属性(property)
-充分体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,即借助于get和set对属性的值进行读写;另一方面还可以对数据的访问属性进行控制(当然也可以通过对普通域加readonly关键字来实现。
-设计原则:属性封装了对域的操作。把要访问的域设为private,通过属性中的get和set操作对域进行设置或访问。
-不能把属性作为引用类型或输出参数来进行传递。
-get方法没有参数;set方法有一个隐含的参数value。除了使用了abstract修饰符的抽象属性,每个访问器的执行体中只有分号“;”外,其他的所有属性的get访问器都通过return来读取属性的值,set访问器都通过value来设置属性的值。
-采用间接方式来访问对象的属性(间接调用get、set方法):对象.属性 = 值(调用set),变量 = 对象.属性(调用get)。
-在属性的访问声明中:
只有set访问器,表明该属性是只写的。
只有get访问器,表明该属性是只读的。
既有set访问器,又有get访问器,表明该属性是可读可写的。
private string s_filename;
public string Filename
{
get
{
return s_filename;
}//get
set
{
if(s_filename!=value)
{
s_filename = value;
}//if
}//set
}//Filename
}
l 属性和字段的比较:
Ø 属性不能使用ref/out 型参数
Ø 属性使用前必须赋值
属性使用前必须赋值,例如:
Time lunch;
lunch.Hour = 12;//错误,lunch没有初始化
属性vs.函数
l 相似点
Ø 都包含执行代码
Ø 都可以有访问修饰符
Ø 都可以有virtual, abstract, override 修饰符
Ø 都可以用在接口中
l 不同点
Ø 属性只能拥有get/set 语句
Ø 属性不可以是void 型
Ø 属性不能使用参数
Ø 属性不能使用[ ] 参数
Ø 属性不能使用括号
public int Hour
{
...
set
{
if (value < 0 || value > 24)
throw new ArgumentException("value");
hour = value;
}
类的属性称为智能字段,类的索引器称为智能数组。由于类本身作数组使用,所以用
this作索引器的名称,索引器有索引参数值。例:
using System;
using System.Collections;
class MyListBox
{
protected ArrayList data = new ArrayList();
public object this[int idx] //this作索引器名称,idx是索引参数
{
get
{
if (idx > -1 && idx < data.Count)
{
return data[idx];
}
else
{
return null;
}
}
set
{
if (idx > -1 && idx < data.Count)
{
data[idx] = value;
}
else if (idx = data.Count)
{
data.Add(value);
}
else
{
//抛出一个异常
}
}
}
}
}
尽可能编写出运行效率更高,更健壮,更容易维护的C#代码。
尽可能的使用属性(property),而不是数据成员(field)。
private int property1
public int Property1
{
get
{
return property1 ;
}
set
{
if (value>1) //这里校验
property1= value ;
else
property1=1;
}
}
//实例属性,可读可写
public int StrCount
{
get
{
return m_strCount;
}
set
{
if (value>m_strCount)
{
strArray = new string[value];
for (int i=0;i<value;i++)
{
strArray[i] = String.Format("String No.{0}",i);
}
m_strCount = value;
}
}
}
private static string m_strName = "MyClass";
//一个静态属性,只读
public static string ClassName
{
get
{
return m_strName;
}
}
class B
{
private A _a;
public A item
{
get
{
if(_a=null)
_a=new A();
return _a;
}
set{_a=value;}
}
}
成员方法:
成员变成员变量、实例变量、类变量、成员方法、实例方法、类方法的区别 量、实例变量、类变量、成员方法、实例方法、类方法的区别
|
简单来说:
类体的定义包括成员变量的定义和方法的定义。
1、成员变量包括实例变量和类变量;而成员方法包括实例方法、类方法,当然还有一种特殊的构造方法。
2、类变量、类方法就是类中的变量、方法,必须是静态的,要加static;故其又称静态变量、静态方法。
3、成员变量、成员方法是对象或实例中的变量、方法,不加static;
类变量:静态域,静态字段,或叫静态变量,它属于该类所有实例共有的属性,在内存中只有一个地方存储这个变量。而且所有的实例都可以修改这个类变量的值(前提是这个类变量没有被final修饰,否则是常量了),而且访问类变量的时候不用实例,直接用类就可以了。
类方法:和类变量一样,可以不用实例,直接用类就可以调用类方法。
实例变量:实例域,实例字段,或叫成员变量。
实例方法:或叫成员方法,必须先有实例,然后才能通过实例调用该实例方法。
使用方法:类方法可以直接调用类变量和类方法
类方法不可以直接调用实例变量和实例方法
类方法中没有this,因为没有实例,this不知道调用哪个实例
类方法可以从类里面直接访问类成员
实例方法可以调用类方法,访问类变量,但是不提倡这样做,会把类方法和类变量混淆成实例方法和实例变量
程序实例:
class AnIntegerNamedX {
int x; //这个是成员变量
public int x() { //成员方法
return x;
}
public void setX(int newX) { //成员方法
x = newX;
}
}
public class Practice{
public static void main(String args[]){
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
}
}
2:
class AnIntegerNamedX {
static int x; //这里写成static,结果就变成全是2了,因为类变量是被实例所共享的,包括myX和anotherX
调用anotherX时所有的所有实例都改变了。
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
public class Practice{
public static void main(String args[]){
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2; //这里把X的值改成2,所有实例的X都是2了,因为类变量是被所有实例共享的,任何实
例都可以对类变量做最终的修改
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
}
}
3:同样的,当把那两个方法都加上static变成类方法的时候,会提示错误让你把X变成静态,是应为类方法不能直接访问实例变量!
构造函数:
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
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
|
#include <iostream> using namespace std; class time { public : time () //constructor.构造函数 { hour=0; minute=0; sec=0; } void set_time(); void show_time(); private : int hour,minute,sec; }; int main() { class time t1; t1.show_time(); t1.set_time(); t1.show_time(); return 0; } void time ::set_time() { cin>>hour>>minute>>sec; } void time ::show_time() { cout<<hour<< ":" <<minute<< ":" <<sec<<endl; } |
1
2
3
4
5
6
7
8
9
|
<?php classBaseClass{ function__construct(){ print "InBaseClassconstructor/n" ;}} classSubClassextendsBaseClass{ function__construct(){ parent::__construct(); print "InSubClassconstructor/n" ;}} $obj =newBaseClass(); $obj =newSubClass(); ?> |
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
|
< html > < body > < script type = "text/javascript" > var test=newBoolean(); if(test.constructor==Array) { document.write("ThisisanArray"); } if(test.constructor==Boolean) { document.write("ThisisaBoolean"); } if(test.constructor==Date) { document.write("ThisisaDate"); } if(test.constructor==String) { document.write("ThisisaString"); } </ script > </ body > </ html > |