<?php
/*
* 类的学习
*
*

class 类名{
    //用关键字" var "来声明变量,即创建了类的属性
    var xxx = ;
    //声明函数,即创建了类的方法。
    function yyyy(参数列表){
    
    }
}

//用new关键字来生成一个对象。
$对象名称 = new 类名称();

//成员的访问
对象->属性 $对象名称->xxx;
对象->方法 $对象名称->yyy();

$this 就是对象内部代表这个对象的引用
$this->属性 $this->xxx;
$this->方法 $this->yyy();

PHP5:构造函数的声明必须是__construct( ) ,
以前的版本中,构造函数的名称必须与类名相同
格式:function __construct ( [参数] ) {}
一个类中只能声明一个构造方法,在每次创建对象的时候都会去调用一次构造方法,
不能主动的调用这个方法,所以通常用它执行一些有用的初始化任务。

析构函数是 PHP5 新添加的内容,
允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件,释放结果集等,
析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,也就是对象在内存中
被销毁前调用析构函数.
一个类的析构函数名称必须是__destruct(){内部操作}。析构不能带有任何参数

封装性是把对象的属性和服务结合成一个独立的
相同单位,并尽可能隐蔽对象的内部细节:1.把对象的全部属性和全部服务结合在一
起,形成一个不可分割的独立单位(即对象).2.信息隐蔽,即尽可能隐蔽对象的内部细节,对外形
成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系

封装可以用三个子来概括:私有化.具体来说,是通过访问修饰符,将类中不需要外部访问的属性和方法进行私有化处理,来实现访问控制.

封装的作用主要有两点,一是方法封装,即将使用者关注的功能暴露,而隐藏其他使用者用不到的功能;二是属性封装,即对用户的数据进行控制,防止不合法的数据传输设置.

对于方法的封装,通过对方法前加private关键字来进行设置,即进行私有化处理.
对于属性的封装,可以有两种方法来进行实现.
一是通过自己提供的set/get方法来对私有化的属性进行设置/读取
二是通过魔术方法来进行实现

使用private这个关键字来对属性和方法进行封装
private $xxx; 
private function yyy(){...}

//定义一个构造方法参数为私有的属性
function __construct($xxx)
{
//通过构造方法传进来的$xxx给私有成员属性$this->xxx赋初使值
$this->xxx=$xxx;
}

构造方法不要设置成私有的,在类的外面可以访问到,
这样就可以使用构造方法创建对象,另外构造方法也是类里面的函数,可以用构造方法给私有的属性赋初值

在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值其
属性,以及检查属性的“__isset()”和删除属性的方法“__unset()”

//__get()方法用来获取私有属性
private function __get($property_name)
{
    if(isset($this->$property_name)){
        return($this->$property_name);
    }else{
        return(NULL);
    }
}
//__get()方法:这个方法用来获取私有成员属性值的.私有属性已经被封装上了,是不能
直接获取值的(比如: “echo $p1->name”这样直接获取是错误的),但是如果在类里面加上了这
个方法,在使用 “echo $p1->name” 这样的语句直接获取值的时候就会自动调用__get($property_name)
方法,将属性name传给参数$property_name,通过这个方法的内部执行

//__set()方法用来设置私有属性
private function __set($property_name, $value)
{
    $this->$property_name = $value;
}

__set()方法:这个方法用来为私有成员属性设置值的,有两个参数,第一个参数为你要为设置
值的属性名,第二个参数是要给属性设置的值,没有返回值

没有__set()这个方法,是不允许的, 比如:$this->name=‘zhangsan’, 这样会出错,但是如果你在类里面加上了__set($property_name, $value)这个方法,在直接给私有属性赋值的时候,会自动调用它

isset()是测定变量是否设
定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false
private function __isset($nm)
{
echo "当在类外部使用isset()函数测定私有成员$nm时,自动调用";
return isset($this->$nm);
}

在一个对象里面加上“__unset()”这个方法,就
可以在对象的外部去删除对象的私有成员属性了。在对象里面加上了“__unset()”这个方法之后,
在对象外部使用“unset()”函数删除对象内部的私有成员属性时,自动调用“__unset()”函数来帮
我们删除对象内部的私有成员属性,这个方法也可以在类的内部定义成私有的

继承:让子类继承父类中的非私有属性和方法
class 子类 extends 父类{
    
}
子类继承父类后,相当于将父类的属性和方法copy到子类,可以直接使用$this调用
PHP只能单继承,不支持一个类继承多个类,但是一个类可以进行多层继承

重载就是子类覆盖父类的已有的方法
因为PHP是弱类型的语言,所以在方法的参数中本身就可以接收不同类型的数据, 又因为 PHP
的方法可以接收不定个数的参数所以通过传递不同个数的参数调用不相同方法名的不同方法也是不成立的-不能重载

一种是使用父类的“类名::”来调用父类中被覆盖的方法;
一种是使用“parent::”的方试来调用父类中被覆盖的方法;

//使用父类的“类名::“ 来调用父类中被覆盖的方法;
// Person::say();
//或者使用“parent::” 的方试来调用父类中被覆盖的方法;
parent::say();
//加上一点自己的功能
....

PHP5支持如下3种访问修饰符:
public(公有的、默认的),private(私有的)和protected(受保护的)三种

public公有修饰符,类中的成员将没有访问限制,所有的外部成员都可以访问(读和写)这
个类成员(包括成员属性和成员方法)

private 私有修改符,被定义为private的成员,对于同一个类里的所有成员是可见的,即是
没有访问限制;但对于该类的外部代码是不允许改变甚至读操作,对于该类的子类,也不能访问private修饰的成员。

protected 保护成员修饰符,被修饰为 protected 的成员不能被该类的外部代码访问.但是对
于该类的子类有访问权限,可以进行属性、方法的读及写操作,该子类的外部代码包括其的子类都
不具有访问其属性和方法的权限

final 关键字只能用来定义类和定义方法,不能使用 final这个关键字来定义成员属性,因为
final是常量的意思,在PHP里定义常量使用的是define()函数,所以不能使用final来定义成员属性
使用final关键标记的类不能被继承
final class Person
{
    … …
    final function say(){
        
    }
}

Static关键字是在类中描述成员属性和成员方法是静态的,固定的;静态属性在内存中
就只有一个,而让这几百个或更多的对象共用这一个属性,static成员能够限制外部的访问,因为
static的成员是属于类的,是不属于任何对象实例,是在类第一次被加载的时候分配的空间,其他
类是无法访问的,只对类的实例共享,能一定程度对类该成员形成保护。

//下面是人的静态成员属性
public static $myCountry="中国";

//输出静态属性
echo Person::$myCountry;

//重新给静态属性赋值
Person::$myCountry="美国";
echo Person::$myCountry;

在静态方法中访其它静态成员我们使用的
是一个特殊的类“self”;self 和$this 相似,只不过self是代表这个静态方法所在的类.所以
在静态方法里,可以使用这个方法所在的类的“类名”,也可以使用“self”来访问其它静态成员,
如果没有特殊情况的话,我们通常使用后者,即“self::成员属性”的方式-非静态成员也可以

const是定义常量的关键字,在PHP 中定义常量使用的是define()这个函数,但是在
类里面定义常量使用的是 “const”这个关键字,用“const”修饰的成员属性的访问方式和“static”修饰的成员访问的方式差
不多,也是使用“类名”,在方法里面使用“self”关键字.但是不用使用“$”符号,也不能使
用对象来访问.
const constant = 'constant value';
function showConstant() {
echo self::constant . "\n"; //使用 self访问,不要加” $”
}

“__toString()”方法可以自动被调用的,是在直接输出对象引用时自动调用的,如: “$p=new Person()”中,$p就是一个引用,不能使用 echo直接输出$p,这样会输出“Catchable fatal error: Object of
class Person could not be converted to string”这样的错误,如果在类里面定义了“__toString()”方法,在直接输出对象引用的时候是自动调用了“__toString()”方法,输出“__toString()”方法中返回的字符,一定要有个返回值(return语句).
public $foo;
public function __construct($foo) {
    $this->foo = $foo;
}
/定义一个__toString方法,返加一个成员属性$foo
public function __toString() {
    return $this->foo;
}

在一个项目里面,使用两个或多个一样的对象,如果使用“new”关键字重新创建对象的话,再赋值上相同的属性,这样做比较烦琐所以要根据一个对象完全克隆出一个一模一样的对象,是非常有必要的,而且克隆以后,两个对象互不干扰.
在PHP5中我们使用“clone”这个关键字克隆对象
$p1=new Person("张三","男",20);
//使用“clone” 克隆新对象p2,和p1对象具有相同的属性和方法。
$p2=clone $p1;
$p2->say();

function __clone()
{
    //$this指的复本p2,而$that是指向原本p1,这样就在本方法里,改变了复本的属性。
    $this->name="我是假的$that->name";
    $this->age=30;
}

$p1=new Person("张三","男",20);
$p2=clone $p1;
$p1->say();
$p2->say();

在程序开发中,如果在使用对象调用对象内部方法时候,调用的这个方法不存在那么程序就会
出错,然后程序退出不能继续执行.那么可不可以在程序调用对象内部不存在的方法时,提示我们
调用的方法及使用的参数不存在,但程序还可以继续执行,这个时候我们就要使用在调用不存在的
方法时自动调用的方法“__call()”.

function __call($function_name, $args)
{
    print "你所调用的函数:$function_name(参数:";
    print_r($args);
    print ")不存在!<br>\n";
}

在OOP语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为外部代码
访问其的接口而抽象方法就是为了方便继承而引入的.
什么是抽象方法?在类里面定义的没有方法体的方法就是抽象方法,所谓的没有方法体指的是, 在方法声明的时候没有大括号以及其中的内容, 而是直接在声明时在方法名后加上分号结束,
另外在声明抽象方法时还要加一个关键字“abstract”来修饰;
例如:
abstract function fun1();
abstract function fun2();
那么什么是抽象类呢?只要一个类里面有一个方法是抽象方法,那么这个类就要定义为抽象类,抽象类也要使用“abstract”关键字来修饰;在抽象类里面可以有不是抽象的方法和成员属性,但只要有一个方法是抽象的方法,这个类就必须声明为抽象类,使用“abstract”来修饰.

抽象类不能产生实例对象,所以也不能直接使用
声明抽象类有什么用呢?将抽象方法是作为子类重载的模板使用的,定义抽象类就相当于定义了一种规范,这种规范要求子类去遵守,子类继承抽象类之后,把抽象类里面的抽象方法按照子类的需要实现.子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,那么子类还是抽象类,还是不能实例化对;为什么我们非要从抽象类中继承呢?因为有的时候我们要实现一些功能就必须从抽象类中继承,否则这些功能你就实现不了,如果继承了抽象类,就要实现类其中的抽象方法;

abstract class Demo
{
    var $test;
    abstract function fun1();
    abstract function fun2();
    function fun3()
    {
        … .
    }
}
$demo=new Demo(); //抽象类为能产生实例对象,所以这样做是错的,实例化对象交给子类
class Test extends Demo
{
    function fun1()
    {
        …
    }
    function fun2()
    {
        …
    }
}
$test=new Test(); //子类可以实例化对象,因为实现了父类中所有抽象方法

php5 接口技术
不支持多重继承.也就是说每个类只能继承一个父类.为了解决这个问题,PHP 引入了接口接口的思想是指定了一个实现了该接口的类必须实现的一系列
方法.接口是一种特殊的抽象类,抽象类又是一种特殊的类,所以接口也是一种特殊的类,为什么
说接口是一种特殊的抽象类呢?如果一个抽象类里面的所有的方法都是抽象方法,那么就换一
种声明方法使用“接口”;也就是说接口里面所有的方法必须都是声明为抽象方法,另外接口里面
不能声明变量,而且接口里面所有的成员都是public 权限的.
声明一个类的时候我们使用的关键字是“class”,而接口一种特殊的类,使用的关键字是“interface”;
类的定义:class 类名{ … }
接口的声明:interface 接口名{ … }

//定义一个接口使用interface 关键字,“ One” 为接口名称
interface One
{
    //定义一个常量 在接口里面不能用变量成员,要使用 const 这个关键字声明
    const constant = 'constant value';
    //定义了一个抽象方法”fun1”
    public function fun1();
    //定义了抽象方法”fun2”
    public function fun2();
}

接口是一种特殊的抽象类,里面所有的方法都是抽象方法,所以接口也不能产生实例对象;
它也作为一种规范,所有抽象方法需要子类去实现。
可以使用“extends”关键字让一个接口去继承另一个接口;

//使用”extends” 继承另外一个接口
interface Two extends One
{
    function fun3();
    function fun4();
}

//使用“implements” 这个关键字去实现接口中的抽象方法
class Three implements One
{
    function fun1()
    {
        … .
    }
    function fun2()
    {
        … .
    }
}
//实现了全部方法,去可以使用子类去实例化对象了
$three=new Three();

也可以使用抽象类,去实现接口中的部分抽象方法,但要想实例化对象,这个抽象类还要有子类把它所有的抽象方法都实现才行;

//使用implements实现多个接口
class Four implemtns 接口一,接口二,… .
{
    //必须把所有接口中的方法都要实现才可以实例化对象。
}

PHP中不仅一个类可以实现多个接口,也可以在继承一个类的同时实现多个接口,一定要先继承类再去实现接口;

//使用extends继承一个类,使用implements 实现多个接口
class Four extends 类名一implemtns 接口一,接口二,… .
{
    //所有接口中的方法都要实现才可以实例化对象
    … … … ..
}

多态是除封装和继承之外的另一个面向对象的三大特性之一,PHP中虽然可以实现
多态,但和C++还有Java这些面向对象的语言相比,多态性并不是那么突出,因为PHP本身就是一
种弱类型的语言,不存在父类对象转化为子类对象或者是子类对象转化为父类对象的问题,所以多
态的应用并不是那么的明显;所谓多态性是指一段程序能够处理多种类型对象的能力.
对于面向对象的程序来说,多态就是把子类对象赋值给父类引用,然后调用父类的方法,去
执行子类覆盖父类的那个方法,但在PHP里是弱类型的,对象引用都是一样的不分父类引用,还是子类引用

多态的实现必须要满足三个条件:一是子类继承父类,二是子类重写父类的方法,三是父类引用指向子类对象

//定义形状的接口里面有两个抽象方法让子类去实现
interface Shape
{
    function area();
    function perimeter();
}
//定义了矩形子类实现了形状接口中的周长和面积
class Rect implements Shape
{
    private $width;
    private $height;
    function __construct($width,$height)
    {
        $this->width=$width;
        $this->height=$height;
    }
    function area()
    {
        return "矩形的面积是:".($this->width*$this->height);
    }
    function perimeter()
    {
        return "矩形的周长是:".(2*($this->width+$this->height));
    }
}
//定义了一个圆形子类实现了形状接口中的周长和面积
class Circular implements Shape
{
    private $radius;
    function __construct($radius)
    {
        $this->radius=$radius;
    }
    function area()
    {
        return "圆形的面积是:".(3.14*$this->radius*$this->radius);
    }
    function perimeter()
    {
        return "圆形的周长是:".(2*3.14*$this->radius);
    }
}
//把子类矩形对象赋给形状的一个引用
$shape=new Rect(5,10);
echo $shape->area()."<br>";
echo $shape->perimeter()."<br>";
//把子类圆形对象赋给形状的一个引用
$shape=new Circular(10);
echo $shape->area()."<br>";
echo $shape->perimeter()."<br>";

把对象串行化
有时候需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到
达另一端时,再还原为原来的对象,这个过程称之为串行化
有两种情况必须把对象串行化,第一种情况就是把一个对象在网络中传输的时候要将对象
串行化,第二种情况就是把对象写入文件或是数据库的时候用到串行化.
串行化有两个过程,一个是串行化,就是把对象转化为二进制的字符串,serialize()
函数来串行化一个对象,另一个是反串行化,就是把对象转化的二进制字符串再转化为对象,使用unserialize()函数来反串行化一个对象.
PHP中 serialize()函数的参数为对象名,返回值为一个字符串,Serialize()返回的字符串含
义模糊,一般我们不会解析这个串来得到对象的信息,我们只要把返回来的这个字符串传到网络另
一端或是保存到方件中即可.
PHP中unserialize()函数来反串行化对象,这个函数的参数即为serialize()函数的返回值,
输出当然是重新组织好的对象

$p1=new Person("张三","男",20);
$p1_string=serialize($p1); //把一个对象串行化,返一个字符串
echo $p1_string."<br>"; //串行化的字符串我们通常不去解析
$p2=unserialize($p1_string); //把一个串行化的字符串反串行化形成对象$p2
$p2->say();

在php5中有两个魔术方法__sleep()方法和__wakeup()方法,在对象串行化的时候,会调用一
个__sleep()方法来完成一些睡前的事情;而在重新醒来,即由二进制串重新组成一个对象的时候,
则会自动调用PHP的另一个函数__wakeup(),做一些对象醒来就要做的动作.
__sleep()函数不接受任何参数,但返回一个数组,其中包含需要串行化的属性.末被包含的属性将在串行化时被忽略,如果没有__sleep()方法,PHP将保存所有属性.

//指定串行化时把返回的数组中$name和$age 值串行化,忽略没在数组中的属性$sex
function __sleep()
{
    $arr=array("name","age");
    return($arr);
}

//重新生成对象时,并重新赋值$age为40
function __wakeup() {
    $this->age = 40;
}

自动加载类
很多开发者写面向对象的应用程序时,对每个类的定义建立一个PHP 源文件.一个很大的烦
恼是不得不在每个脚本(每个类一个文件)开头写一个长长的包含文件的列表.
在软件开发的系统中,不可能把所有的类都写在一个PHP文件中,当在一个PHP文件中需要调
用另一个文件中声明的类时,就需要通过include把这个文件引入.不过有的时候,在文件众多的
项目中,要一一将所需类的文件都include进来,是一个很让人头疼的事,所以我们能不能在用到
什么类的时候,再把这个类所在的PHP文件导入呢?这就是我们这里我们要讲的自动加载类。

在 PHP 5 中,可以定义一个__autoload()函数,它会在试图使用尚未被定义的类时自动调用,
通过调用此函数,脚本引擎在PHP出错失败前有了最后一个机会加载所需的类,__autoload()函
数接收的一个参数,就是你想加载的类的类名,所以你做项目时,在组织定义类的文件名时,需要
按照一定的规则,最好以类名为中心,也可以加上统一的前缀或后缀形成文件名,比如
xxx_classname.php、classname_xxx.php以及就是classname.php等等

function __autoload($classname)
{
    require_once $classname . '.php';
}
//MyClass1类不存在自动调用__autoload()函数,传入参数”MyClass1”
$obj = new MyClass1();
//MyClass2类不存在自动调用__autoload()函数,传入参数”MyClass2”
$obj2 = new MyClass2();
*/

/*
php.net demo
*/
class SimpleClass{

// 声明属性
    public $var = 'a default value';
    const var1 = "gaga\n";

// 声明方法
    public function displayVar(){
        echo self::var1;
        echo $this->var."\n";
    }

}

function __autoload($class_name) {
    require_once $class_name.'.php';
}

$a = new SimpleClass();
$a->displayVar();
echo SimpleClass::var1;

$b = new SimpleClass1();

/*
四.
namespace NS {
    class ClassName {

}
    echo ClassName::class;
}
//使用 ClassName::class 可获取一个字符串,包含了类 ClassName的完全限定名称.这对使用了命名空间的类尤其有用
*/
namespace //空间防止类重名.一个文件可以多个空间(不见议)
use //引用的是类

// 全局空间在名称前加上前缀 \ 表示该名称是全局空间中的名称
?>

PHP面向对象笔记(兄弟连)相关推荐

  1. C++ 面向对象笔记——侯捷老师(一)

    此笔记为侯捷老师的C++面向对象课程笔记,在开篇中,老师首先讲C++的类,并将类分为两种基本类型: 1.类的成员不带指针的类.以复数类为例,讲解类的基本知识. 类的基本构成:成员变量和成员函数.构造函 ...

  2. 面向对象笔记2 原型链 解释

    面向对象2 ## js中已经写好的构造函数 * 内置对象 * js提供的已经定义好的对象 * Array,Date,RegExp,String,Number,Boolean * 同时js定义好了对应的 ...

  3. JavaScript高级特征之面向对象笔记

    Javascript面向对象: 函数:         * Arguments对象:             * Arguments对象是数组对象             * Arguments对象的 ...

  4. JavaSE面向对象-笔记

    数组 1.数组的特点 1.其长度是确定的,数组一旦被创建,它的大小就是不可改变的 2.其元素必须相同类型,不允许出现混合类型 3.数组中的元素可以是任何数据类型,包括基本类型和引用类型 4.数组变量属 ...

  5. python OOP面向对象笔记

    文章目录 OOP面向对象编程 面向对象名词 类与对象 类的内容(成员) 类的定义 self 类的变量作用域的问题 1.实例变量 调用 类的变量 (此时实例变量还没单独定义/赋值) 2.实例变量 调用 ...

  6. [52PJ] Java面向对象笔记(转自52 1510988116)

    面向对象概念 面向对象三大特征:封装,继承,多态 面向对象编程(OOP,Object Oriented Programing)是相对于面向过程编程说的,之前写的代码基本都是纯的面向过程编程的,当项目复 ...

  7. pyhton面向对象笔记

    面向对象(OOP)基本概念 面向对象编程 -- Object Oriented Programming 简写 OOP 目标 了解 面向对象 基本概念 01. 面向对象基本概念 我们之前学习的编程方式就 ...

  8. Java面向对象笔记 • 【第3章 继承与多态】

    3.1.1 自定义包 如果在程序中没有声明包,类将被存放在default的包中,该方式不被提倡. 语法: package 包名 包命名规范: 包的命名规范应当体现出项目资源良好的划分 自定义标签类所在 ...

  9. 山东大学面向对象笔记 整理

    //考试主要在设计模式,设计原则上考察较多,多看看老师ppt的类图,自己手动画画 //问答题: 四种多态变量形式,继承的八种,三种空间分配,多重单重继承优劣.... 第一部分  对象,类等基础概念 1 ...

  10. Python面向对象-笔记

    面向对象Object Oriented 概述 面向过程 \1. 分析出解决问题的步骤,然后逐步实现. 例如:婚礼筹办 – 发请柬(选照片.措词.制作) – 宴席(场地.找厨师.准备桌椅餐具.计划菜品. ...

最新文章

  1. crt python_SecureCRT Python
  2. 递归遍历文件夹,并添加到TreeView控件中
  3. 来自Riot 的一份游戏美术教程(一):核心原则
  4. SAP UI5应用访问OData metadata的url和Destination
  5. python serial 发送ctl+c_[已解决]shell 脚本 给命令发送 Ctrl+C信号
  6. 黯然推荐:王江民先生悼文(文言)
  7. 简单java登录页面android_Android 之路 - RxJava2+Retrofit实现简单登陆
  8. RabbitMQ 集群原理和完善
  9. ElasticSearch SQL 日期函数
  10. app 手机网页一些小知识
  11. QQ连连看外挂--基于LAScript
  12. linux、ubuntu如何查看网速
  13. JAVA编写弹珠(弹珠)小游戏
  14. Windows10 1050Ti cuda toolkit11.3 pytorch-gpu 踩坑教程
  15. 单片机24秒倒计时c语言,单片机汇编语言24秒倒计时程序
  16. C语言学习总结(三)-----函数指针
  17. NKOI 2495 火车运输
  18. 日历代码 谷歌无法显示当日_在Windows日历中显示您的Google日历
  19. 布朗大学计算机专业怎么样,布朗大学计算机专业怎么样?过来人告诉你
  20. VS C++ 共享内存 CreateFileMapping

热门文章

  1. 数字证书及CA的详细理解
  2. 以汉维语音翻译为例-uniapp原生顶部栏维语翻译-使用字体图标
  3. MUSIC算法相关原理知识(物理解读+数学推导+Matlab代码实现)
  4. Matlab遗传算法
  5. AI初学者用什么软件?适合AI新手的9款人工智能开源软件
  6. 蚂蚁课堂二期视频(每特学院二期)
  7. mac 开启android 模拟器,折腾mac的经验积累 2:mac上如何玩安卓模拟器
  8. 《深入浅出数据分析》读后感
  9. testbench实例 vhdl_VHDL TestBench基础(转)
  10. 学生表,选课表,课程表