C/C++特性

1、C

  1. 1 柔性数组(0长数组)
    C语言的数组通常是固定的,这个特定非常不利于动态数据结构的实现,比如linux内核中,好多数据结构需要在设备热插拔时动态增删驱动对应的结构体,此时根本无法预先知道所使用的结构体数组的大小。GNU/GCC 在标准的 C/C++ 基础上做了有实用性的扩展, 其中的零长度数组(Arrays of Length Zero)针对上述场景就十分合适。
struct zero_buffer
{
    int     len;
    char    data[0];
};

char data[0]; 只是个数组名, 是不占用存储空间的,

即 sizeof(struct zero_buffer) = sizeof(int),但是一定要确保data一定位于最后成员位置。具体怎么来用呢?

 //  开辟
    if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
    {
        zbuffer->len = CURR_LENGTH;
        memcpy(zbuffer->data, "Hello World", CURR_LENGTH);


        printf("%d, %s\n", zbuffer->len, zbuffer->data);
    }
 //  销毁
	free(zbuffer);
	zbuffer = NULL;  

首先要明确,data长度为0,那么他实际不拥有任何内存,所以使用时一定要malloc,使用完毕要free,那么柔性数组比直接从堆上申请内存有什么优势呢?答案是保持数据结构的一致性。例如linux驱动程序中设备信息对应的的数据结构必须要是固定的格式,但是各个vendor的产品信息长度又不统一,为了不造成内存浪费,却又必须采用动态内存管理,此时便出现了矛盾。若结合柔性数组,那么所有驱动程序的使用者都可以使用相同的数据结构来对应不同的设备。
具体的做法是,设备驱动在probe时,采用固定的数据结构来存放自己的信息,动态申请内存。其它驱动使用者使用同样的数据结构来读取该内容,设备区卸载时再free掉该内存,神不知鬼不觉的完成堆内存的使用。对于驱动使用者来讲则是透明的。

2、C++

  • 1、内存
    C++类似C语言,变量定义、类实例化是区分存储位置的
    全局/局部定义的变量与实例化对象是在栈上,new出的实例化对象则在堆,此时栈上放的是实例化对象的指针,此时类似于java的引用变量
    但是new出的对象必须自己delete,尽管程序退出后会回收堆内存,但是一定要自己释放,因为有些程序是常驻的。
    C++的引用,是为变量/实例化对象起别名,因此定义时必须指定指代的对象,后面不能更改,这样以后就是修改指代对象本身而不是副本

  • 2、构造函数
    构造函数的构造顺序:
    首先是全局变量或者实例化对象
    然后是就近的父类
    最后是子类
    局部静态变量只定义初始化一次,后面循环到该定义处也不会再次初始化

  • 3、拷贝构造
    当对象中含有指针或类作为数据成员的,对于已有的对象在构造出新的对象后,仍需要对该已有对象进行引用或利用的,
    需要我们自己定义拷贝构造函数(进行深拷贝)。简单说就是利用已有实例化拷贝处另一个实例化,两者都保留
    这样可保证,两个实例化对象中new处的堆内存各自都有一份,放置析构是对同一堆内存delete两次

  • 4、移动构造
    而对于已有的对象在构造出新的对象后,将不再对该已有对象进行引用或利用的,
    需要我们自己定义移动构造函数(进行浅拷贝),即利用已有实例构造新的实例,原实例指向(指针指向的内容)清空

  • 5、左右值引用
    当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
    左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的对象,即左值持久,右值短暂

  • 6、静态成员
    静态成员类内只是声明,需要在类外定义加初始化
    静态成员函数只能访问静态成员,他可以类内申明,类外定义

  • 7、引用返回
    返回值是子函数创建的临时变量(代码里虽然是返回形参,实际上是另一个临时变量,除非是指针),调用函数使用完返回值后,该返回值会被自动销毁
    因此可以返回形参的引用,这样就不会创建临时变量。当然形参也要指定为引用,否则形参传递过来就已经是副本了
    可以看出引用非常类似于指针,都是在同一内存区域进行改写,而不创建新的临时变量
    另外引用返回后,可以继续调用该返回值的成员方法,若是返回局部对象,那是不行的。

  • 8、访问控制
    private 类私有,子类与类使用者都不能直接访问 (父亲有钱,儿子外人都不能随便拿)
    protected 类私有,但是开放给子类,只有类使用者不能直接访问(父亲有家的钥匙,儿子可以直接用,外人不行)
    public 公有, 都可以直接访问(父亲修了一条路,儿子护着外人都可以走这条路)
    使用时权限调整:
    子类可以调整自己可以直接访问的属性的权限,可以把权限提升也可降低(public: using Father::key,表示把key设置为所有人都可用状态)
    继承时权限调整:
    子类继承父类时可以指定继承方式,当然只能影响子类有权限访问的属性(父类的proteced与public属性)
    子类继承方式会影响孙子类以及类使用者对于属性的访问权限
    向上转换:
    形参为父类的函数,可以传递进去子类,由于子类与父类的成员内存分布是类似的(子类在后面多了一些自己的成员),
    当然,实参使用时完全是使用父类的方法与属性,并不会查找子类重载的属性方法。

  • 9、虚继承
    一个子类可同时继承多个父类,继承两者的所有方法属性,但是两个父类有同名的属性则会出现二义性,查找时会报错
    可以把两个父类的公共部分提取出来,定义到一个父类中,C继承B1与B2,B1与B2继承A,并且B1与B2继承A时要加关键字virtural
    在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。
    例如iValue这个成员,从类 C这个角度上来看,它是从类B1与类B2继承过来的,而类B1、 B2又是从类A继承过来的,但它们只保留一个副本
    注意用作虚拟基类的类没有特殊性,既可以被虚继承也可以被普通继承

  • 10、虚函数(多态的一种实现)
    子类override父类方法,但是若声明一个父类类型的指针,分别指向一个子类实例与一个父类实例,去调用两者均实现的方法,
    会发现只会调用父类的方法,因为这是静态联篇规定的编译器行为(类似于形参父类,传入子类实参,但实际仍然只能找到父类方法)。
    在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,
    而是动态决定将要调用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。
    但派生类中的virtual关键字可以省略,也表示这是一个虚函数,这就是所谓的动态联篇。
    另外虚函数返回值为指针或者引用,返回当前对象的指针或者引用时也是可以的。

  • 11、纯虚函数
    继续上面的虚函数,通常来说父类只提供统一框架接口,没有必要实现方法,那么可以在定义函数时在结尾加个 =0,这就是纯虚函数
    包含纯虚函数的类成为抽象类,他们不能被实例化,只能在父类继承并实现其中的方法后才能被实例化

  • 12、类型转换

    const_cast<>
    可以将可读可写属性进行更改

    reinterpret_cast<>
    相当于C风格的小括号实现的强制类型转换

    static_cast<>
    编译时的转换,
    上行转换:子类指针到父类指针,这种转换很常见很安全 此时与dynamic_cast效果一致,建议使用
    下行转换:静态编译时执行,因此不能判断处是否后面有调用子类特有方法,存在极大安全隐患,不建议使用

    dynamic_cast<>
    运行时的转换
    含有虚函数的类,保存有一份虚函数表和类信息(本类信息与继承信息),对于这种类可以实现类型的转换
    一般转换之发生在指针之间、引用之间,可分为上行转换与下行转换
    上行转换:子类指针到父类指针,这种转换很常见很安全 此时与static_cast效果一致,但消耗更大,不建议使用
    下行转换:父类指针到子类指针,建议使用
    另外转换指针时可以通过判断转化后的指针是否为空,来判断
    转换是否成功,而引用不行,所以一般不对引用左向下类型转换。只有指针指向的是一个子类对象时,将该指针转化为
    子类对象指针才成功,否则一定失败。

  • 13、抽象类
    存在任意一个纯需函数,那么就是抽象类,抽象类是为子类提供框架的,本事不能被实例化(纯虚函数,实例化了也没有意义)
    其子类继承抽象类后必须实现其中的所有纯虚函数
    例如抽象类包各个基类的公共方法,将其定义为纯虚函数,然后定义多个全局函数,各个子类实现有关自己的那个全局函数,
    实际就是提供返回创建自己实例化对象的函数接口。这样应用程序只需要包含抽象类,并调用抽象类中不同的全局函数,间接实例化各个子类对象
    实例化的子类对象都赋值给抽象的引用(子类可向上隐式转化)
    在分别调用抽象类引用时,由于纯虚函数的特性,会发生动态绑定(根据引用实际引用的对象类型查找虚函数对应子类的版本)
    这样可以实现,只要抽象类头文件不改变,子类头文件或者源文件怎么变,都不需要从新编译应用程序,只需要重新编译子类对应的共享库即可

  • 14、 抽象类界面(业务分离)
    利用抽象类向类应用程序提供固定接口(各继承类的实例化接口),各个继承类合并编译为一个动态库,可执行程序编译时原材料变为应用程序源文件+类库
    应用程序不需要知道各个继承类的存在,仅通过抽象类结合动态联篇原理就可以调用不同继承类的方法
    另外,只要抽象类的头文件源文件不改变,无论继承类怎么修改,仅编译动态库即可,应用程序无需重新编译

  • 15、模板编程
    1、函数模板
    不同于java的泛型方法,C++可以定义全局函数(与类没有任何关系)。
    函数模板解决的问题:相比函数重载,更加减少代码量(将功能相似的但处理的数据类型不同的函数合并为一个模板)
    template void Swap(T& a, T& b)
    使用函数模板时,可以指定模板类型后由编译器实例化函数,也可以不指定,而由编译器自动推导并实例化
    注意:函数模板不是宏,指定不同的模板类型越多,实例化出的函数越多,这是占内存的(编译时确定)。

    2、类模板
    类模板作用类似于函数模板,类也存在仅有成员类型不同,但是功能类似的属性与方法,例如处理表示栈的类,栈可以处理int也可以处理float,
    但是所有的数据类型处理方式基本一致,无非出栈、压栈,只是类型不同而已
    template class Test{……} //类模板
    template void Test::print() //类模板成员方法,比函数模板多了个Test::所属域而已
    int Test::cnt=0; //类模板静态成员的初始化

    3、特化
    有些数据类型,即使满足函数模板或者类模板的参数类型匹配,编译器也能实例化,但是却不能正确处理,例如普通类型比较相等时
    直接使用= =即可,字符串则需要strcmp()函数,针对这种情况可以对字符串类型的函数模板进行特化:

    template<> // 此处不添加类型模板,直接使用空即可
    bool Isequal<char*>(char*& p1, char*& p2){
    return strcmp(p1, p2) == 0;
    }
    实际上,直接定义出这个函数不适用模板也是可以的,因为编译器会优先匹配普通函数,继而匹配模板函数并实例化

    类也可以特化,而且分为全特化(所有参数类型特化为具体类型)偏特化(部分参数类型特化为具体类型)

  • 16、设计模式

    1、工厂模式

    顾明思议,工厂模式是用来提供统一的接口,用来创建不同但又相关的实例

    1.1、自主工厂模式
    用户使用时,只会固定用到工厂生产的某一产品。此时可以使用数据分离,在工厂中分case生产不同的产品,提供给使用者相同的生产与使用接口
    类似于消费者买一部手机,工厂生产一部手机,说明书中提供固定的打电话、发短信、上陌陌的方法,但是手机内部零部件则是工厂老板根据成本限制来选取的
    优点:消费者方便
    缺点:工厂需要精通各个芯片商的适配
    1.2、抽象工厂模式
    用户不仅购买手机,还在意手机的配置,并且在使用过程中会随机的选取各种配置的手机来使用。
    工厂无法左右用户喜好,只能将手机的生产配置转手给各个芯片提供商完成,并且用户在购买手机时,会按照特定型号,此时工厂的的生产方法为纯虚函数
    真正调用的生产方法是根据动态联篇原则,找到各个外包公司的生产方法
    优点:消费者有选择权,工厂也不需要太多技术含量
    缺点:每个手机芯片方案商都要学会如何适配自己的芯片

    2、策略模式
    策略与使用策略的用户完全分离,无继承关系,用户想用哪个策略,选择之即可

    一个抽象策略类,定义策略(手机娱乐相关)但不实现,定义多个策略类(看电影策略、听音乐策略)并实现策略
    定义角色(无聊的学生),需要完成的事情是如何用手机打发时间
    学生翻看手机功能,找到任意个支持的功能(对应策略类),打开该功能(实例化一个策略类),开始打发时间

    优点:任意关联,用户可以使用任意一个策略
    缺点:用户要知道并熟悉所有策略,否则无法选择

    3、适配器模式
    经典思维(没有兼容不了的软件,如果有就分层)
    用户接口与已有的实现接口不匹配,可以在中间加一个适配器(也就是加一层),一般都是一个抽象类,适配用户接口,多个继承类分别继承抽象类与被适配的类
    继承类使用被适配类提供的接口override抽象类的接口

    4、单例模式

    提供可访问接口,用于实例化自己,且整个程序运行期间都只有一个实例化对象。(注意线程安全)

    5、代理模式

    被操作对象(女孩),操作对象(宅男),搭讪类型,代理对象(媒婆)
    宅男知道各种搭讪类型的操作步骤,但是不好意思出手,媒婆脸皮厚,但是不知道搭讪步骤
    媒婆拿到后补女孩名字,实例化自己(构造内部实例化宅男,并把女孩名字给到宅男心中记录),搭讪时,其实是调用宅男为女孩量身定制的方法。
    这样便实现了代理的目的

    6、外观(包装)模式

    十分简单的设计思路:一台电脑的所有组件在开机关机是都执行一些操作,这些组件全部作为电脑类的私有对象,只把电脑类暴露给用户
    用户调用电脑的开关机时,由电脑类完成各组件的相关调用

    7、观察值模式

    观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都要得到通知并自动更新。
    观察者模式从根本上讲必须包含两个角色:观察者和被观察对象。

    被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
    观察者对象可以注册到被观察者的中,完成注册后可以主动检测被观察者的变化,或者被动接收被观察者的通知。
    当然观察者也可以被注销掉,停止对被观察者的监控

    8、职责链模式

    把对一件事情的处理分为多个层级(链扣),每个链扣为一个类,最底层类首当其冲,需要提升时,则提交给上级链扣,直到最后一级
    类似于公司请假,发起请假后,人资接收申请,上报组长,组长若能决定,直接签字,流程完毕;若不能决定,则签完字后继续上报给所长,
    以此类推

    9、装饰模式

    对一个组件添加各种装饰,有一个统一的添加装饰类,其它各种类均继承之,并在相同接口内向组价添加自己喜欢的装饰
    优势有:
    需要扩展一个类的功能,或给一个类添加附加职责。
    需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
    需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实

    10、桥接模式

    继承关系中存在二维变化情况时(手机品牌与手机app),直接继承会派生很多子类
    如果以app作为基类,派生出多个app,然后每新出一款手机,就需要继承所有app,假如新开发一款app,那么就需要以该app作为父类,派生市面上的所有手机子类
    如果以手机为基类,情况相同,假如上市一款新手机,则需要,以该手机为父类,继承实现所有主流app
    以上仅针对编程来言,实际手机同时会适配所有APP,app也会适配所有手机。这个问题仅仅是C++建立继承模型是出现的

    桥接模式必须从头开始设计,不能对已有构架修改实现

    实现思路:
    定义手机基类,基类中定义软件软件基类指针,定义各品牌手机子类;定义app基类,基类中定义软件功能接口,定义各种app子类
    用户使用时,实例化一个app,使用该app实例化一个品牌手机,运行该软件时,首先品牌手机按照自身做一些操作(如何启动app)
    从手机基类获取app基类,按照动态绑定原理,找到具体app的执行方法,运行之

3 、Java

Android选择Java作为其主流开发语言,所以了解java语法必不可少,以下是其与c++相比来说一些较特别的语法性质,java为类C语言,因此语法基本相同,但存在差异:

  • C语言变量只有一种存储方式,在栈上(包含指针变量,只不过指针指向的地址空间在堆上)
    而java变量分为基本变量(在栈上)与引用变量(其实为指针),引用变量本身在栈上,包括数组,类实例(实例只是一个指针),
    这种引用变量定义时必须使用new运算符,通过构造函数实现,new其实就包含堆空间分配,只不过这块空间的释放是由垃圾回收完成

  • java面向对象,因此有类与类实例的概念,c语言的全局变量只在源文件生效,可以被源文件中的所有函数调用
    java同样可以这样,但是有更优雅的方法:定义类静态变量,这种变量不属于任何一个实例,但是可以被任意一个类的实例使用,
    且使用的为同一个变量

  • c语言没有初始化入口,但java的类有,那就是构造函数,因此可以把静态变量初始化的工作放到静态构造代码块中执行({}包围的代码段)
    把这段{}包围的代码段,放到构造函数前面,在实例化类时会首先执行,早于构造函数,且只执行一次。这比C++,必须要在源文件中重新声明该静态变量再初始化要方便一些

  • 1、面向对象的三大特性:

  1. 封装
    一个类代表一类事物,里面有方法、属性,他们还可以细分为公共的、私有的、受保护的,因此子类、包想使用某一个类时,会受这些限制,
    这一套机制可以保证方法、属性具有安全的封装性

  2. 继承

    类继承: 子类可以继承父类(extends),使用父类的方法、属性,父类也可以是abstract的抽象类,子类继承时必须实现其中的abstract方法,
    而父类不可单独实例化,这些特性可以很好的整合各个类的功能,把基础操作提取到父类,具体操作提取到子类,框架型的由父类强制指定等
    接口继承: 可以定义接口interface,里面可以放置全局常量(静态常量,final修饰)与抽象方法,其他类实现这些抽象方法(implements)
    出现接口的原因是java仅支持单类继承,而接口可以多继承。某些公共方法会被一部分子类使用,但不是所有子类都使用,他们自然不能直接
    放到父类,这些方法可以放到接口中(定义为抽象的方法),哪些子类需要就implements这些接口,并且一个子类可以implements多个接口,
    解决了类不可多继承的问题

  3. 多态
    类的转换:
    向上转换(子类到父类)
    Son son = new Son();
    Father f = son;
    父类可以用子类的实例化对象来实例化一个父类实例化对象,并且调用两者均定义的方法,但是如果子类已经overrides了方法
    那么父类调用的是子类的方法,这说明父类已经被子类影响了,这一特性可以实现用任一子类对象去调用形参为父类的方法

    向下转换(父类到子类)
    Father f = new Son();
    Son son = (Son)f;
    其实这并不是真正的父类到子类转换,而是子到父类再到子的转化,这样子类特有的方法是可以被发现的
    这种转换的目的是向上转化的更进一步,首先可以实现任一子类对象去调用形参为父类的方法,然后使用instanceof判断传进来的
    实参到底是哪种类类型,若实际是子类,那可以向下转化为特定子类,继而调用子类的特有方法
    方法的重载:
    方法重载其实就是根据同名方法的不同形参在传入实参是动态选择最匹配的方法

  • 2、jar包
    jar是一个归档打包命令,可以把上面的package归档为.jar后缀的压缩包
    jar -cvf 压缩包名称.jar 需要压缩的路径即package

  • 3、CLASSPATH
    CLASSPATH为编译或者运行时的编译器\JVM的源码\class查找路径
    jar包生成后编译主class会提示package中的class找不到,这时需要:
    export CLASSPATH=.:jar包路径 其中.为当前路径,:为路径\文件分隔符
    这样编译时就不会报错

  • 4、补充:
    java中,一个源文件定义多个class,编译后会生成独立的.class文件,所以说一个源文件并不代表一个作用域,类/包子类、包非子类/
    外包子类、外包非子类才是

  • 5、权限

  1. 类成员权限:
    C++中,类的权限涉及本类、子类、非子类,而java为本类、本包(子类/非子类)、非本包子类、非本包非子类,其权限如下:
    同一类 同一包 不同包子类 不同包非子类

    Private yes

    Default yes yes

    Protected yes yes yes

    Public yes yes yes yes

  2. 类权限:
    类权限修饰关键字只有两种情况:
    public class:
    一个java文件中(一个包package)只能声明一个public class,且类名要与文件名一致,其它类名称可随意
    java虚拟机实例通过调用某个类的main()来运行一个Java程序,而这个main()必须是public static void 并接收一个字符串数组作为参数,
    任何拥有这样一个main()的类都可以作为java程序的起点。至于main()不是必须要放在public类中才能运行程序,其它类亦可以同时定义main
    函数,但是同一时间只会有一个main用作被外部调用的总入口。
    这种类可以被本文件其它类使用,也可以被其它文件的类使用,也就是说他的权限是所有package均可访问
    class:
    私有类,其它package无法访问

  • 6、内部类
    内部类的唯一用途,可以访问外部类的私有属性/方法,使用方法:
	class Outer {
		private int a = 10;
	    class Inner {
			public void printInfo(){
				System.out.println("a = "+a);
			}
		}
		
	}

	public class InnerDemo {
		public static void main(String args[]) {
			Outer o = new Outer();
			Outer.Inner i = o.new Inner();			
		}
	}

内部类的实例化有点奇怪,但可以理解,毕竟内部类要访问外部类的属性,那么外部类必须要先实例化
其实有其它方法简化内部类实例化方法

	class Outer {
		private static int a = 10;
		static class Inner {
			public void printInfo(){
				System.out.println("a = "+a);
			}
		}
		
	}
	public class InnerDemo {
		public static void main(String args[]) {
			Outer.Inner i = new Outer.Inner();
			i.printInfo();
		}
	}
	

这种方式可以不首先实例化外部类就实例化内部类,但是有一个限制,那就是内部类只能访问外部类的静态属性且自己也是个静态杜对象。因为,只有静态属性才不需要实例化类就存在

  • 7、匿名内部类
    有时候,内部类只使用一次,因此没有必要定义出来,而是在使用时定义
//A也可以是类
	interface A {
		//注意printInfo实际为abstract抽象方法,由于interface全都是抽象方法,所有可以不写这个关键字
		//抽象类中必须要写
		public void printInfo();
	}

	public class Anony {
		public static void main(String args[])	{
		static int  m =100;
			testFunc(new A(){
				//实例化A时,同时实现了其中的抽象方法,实质是新创建了一个匿名类,并实例化
				//这里的new A()既不是实例化A也不是实例化Anony,而是Anony的一个匿名内部类,编译文件中可以看到
				//叫做Anony$1.class
				//注意匿名内部类为静态对象,只能访问外部类的静态成员
				public void printInfo() {
					System.out.println("Anony's attribute m ="+m);
					}
				});
		}
		public static void testFunc(A a) {
			a.printInfo();
		}
	}
  • 8、泛型:
    java泛型目标类似于C++模板,是编写通用类的方法,但是java的泛型在编译时会将泛型类型擦除还原为特定的变量类型,因此java泛型被称为伪泛型:

    • 使用泛型能写出更加灵活通用的代码
    • 泛型将代码安全性检查提前到编译期

    通常类型擦除后,会将任何类型向上协变为object类型(所有类类型的父类),实质上之后代码只能使用object类导出的方法与属性,
    这样其实是不合理的,毕竟object不包含具体子类的功能,为了防止这种过度擦除,对应出现了泛型的上下限值,上下限就是限制类型擦除的程度,首先理解两个概念:

    • 协变:子类能向父类转换
    • 逆变: 父类能向子类转换

    限定擦除范围使用extend与super和?通配符
    extend:限定参数类型必须为某一个类类型或者该类类型的子类,即上限
    super: 限定参数类型必须为某一个类类型或者该类类型的父类,即下限
    ? :实例化或者形参中,表明通配所有类型
    上面的三者两两组合使用就可以起到即通配又限定的目的:

	static class Food {}
		static class Fruit extends Food {}
		static class Apple extends Fruit {}

		public static  void playFruit(List < ? extends  Fruit> list){
			
			//list存放的是Fruit或者其子类,那么可以从中get并向上协变为Fruit
			Fruit fruit=list.get(0);
			--------------------------------------------------------
			//list若存放Fruit的子类,自然不能随意add其它子类进去
			list.add(new Apple()); // compile error
			//list若存放Fruit的子类,自然不能随意添加父类进去
			list.add(new Fruit());    // compile error
			
			
			//通配符类型无法实例化 
			list = new ArrayList<? extends Fruit>(); // compile error:  
		}
		
		public  static  void playFruitBase(List < ? super  Fruit> list){
			

			//list可能是Fruit,所以不能放父类Food
			list.add(new Food());     // compile error
			
			//list可能是Fruit,以下都是其子类可以add
			list.add(new Fruit());    // compile success
			list.add(new Apple());    // compile success
			
			//通配符类型无法实例化
			list = new ArrayList<? super Fruit>(); // compile error:       

			Fruit object = list.get(0); // compile error
			
		}

PECS原则总结
从上述两方面的分析,总结PECS原则如下:
如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends -通配符;(Producer Extends)
-如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
-如果既要存又要取,那么就不要使用任何通配符。

  • 9、反射
    Class类 代表类的实体,在运行的Java应用程序中表示类和接口
    每个类都对应一个class类实例(多个实例化对应一个类),该实例由虚拟机自动创建,例如
    Class <?> c = String.class
    表示自己实例化一个Class对象,该Class为虚拟机基于String类自动创建的
    有了Class,调用其中的方法就能获得对应类的各种内容:Field 、Method、Constructor

    Field类 代表类的成员变量(成员变量也称为类的属性)
    前面获得某个类的Class实例,进而可以获得其field类实例
    Field[] sField=strClass.getDeclaredFields()
    然后就能获得类对象的信息
    field.getName());
    field.getType().getCanonicalName()

    Method类 代表类的方法同理,获取某个类的方法
    Class c4 = test.getClass()
    Class[] p4 = {String.class};
    Method method = c4.getDeclaredMethod(“welcome”,p4);
    method.setAccessible(true);
    Object arg1s[] = {“欢迎”};
    method.invoke(test,arg1s);

    Constructor类 代表类的构造方法
    Class c4 = test.getClass()
    Class[] p = {int.class,String.class};
    constructors = c4.getDeclaredConstructor§;

Class类 代表类的实体,在运行的Java应用程序中表示类和接口
每个类都对应一个class类实例(多个实例化对应一个类),该实例由虚拟机自动创建,例如
Class <?> c = String.class
表示自己实例化一个Class对象,该Class为虚拟机基于String类自动创建的
有了Class,调用其中的方法就能获得对应类的各种内容:Field 、Method、ConstructorField类 代表类的成员变量(成员变量也称为类的属性)前面获得某个类的Class实例,进而可以获得其field类实例
Field[] sField=strClass.getDeclaredFields()
然后就能获得类对象的信息
field.getName());
field.getType().getCanonicalName()

Method类 代表类的方法同理,获取某个类的方法
Class c4 = test.getClass()
Class[] p4 = {String.class};
Method method = c4.getDeclaredMethod(“welcome”,p4);
method.setAccessible(true);
Object arg1s[] = {“欢迎”};
method.invoke(test,arg1s);

Constructor类 代表类的构造方法
Class c4 = test.getClass()
Class[] p = {int.class,String.class};
constructors = c4.getDeclaredConstructor§;

3、《好代码、坏代码》笔记

1、编程原则考虑:可读性、模块化、可扩展、可重用、可测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值