A4第四章第2节null和成员变量、局部变量的不同初始化第4节 1、null表示变量没有指向任何对象 如:Person p=null; p.sayHello();//结果会抛出异常 2、对于一个为【null的变量】调用【成员方法】都会报异常NullReferenceException。
3、值类型(ValueType):数值类型(int、long、double等)、boolean等基本类型,枚举、结构体。不能为null。String不是值类型。
public void setAge(int age)
{
this.age = age;
}
1、局部变量【必须初始化】,成员变量声明时【默认初始化】,基本数值类型【默认初始值为int】,String等非基本类型【初始化为null】。why?涉及到栈内存和堆内存,以后讲。
A4第四章第2节定义类字段方法及实例化类
第四章第2节 1、字段不加public,定义的方法加public 2、方法一般是大写开头
3、最简单的类:class Person{}
4、增加类变量(字段)(成员变量)和方法:
class Person{ private int age;//成员变量 private String name; public void setAge(int age1)//定义的方法setAge方法,age1为传入的变量 { this.age = age1; } public void setName(String name1)//定义setName方法,name1为传入的变量 { this.name = name1; } public void sayHello()//定义sayHello方法 { Console.WriteLine("大家好,我是"+name+",我今年"+age+"岁了"); } //再提供get方法 }
5、类的实例化: Person lzy = new Person();// lzy.setName("林志颖"); lzy.setAge(80); lzy.sayHello(); //两个对象的内存分配:根据模板拷贝两份。 yzk.sayHello(); new出的每个对象都是一个单独的实例,两个对象之间的成员变量是独立的两份。new出来的叫【类对象】或者【实例(Instance)】。 6、我们定义一个【类】后,就必须实例化才能使用。【实例化】就是创建一个【对象】的过程。 在C#中,我们使用new运算符关键字来创建。 7、 类 对象=new 类(); 即 class object=new class(); 即Person xcl=new Person(); 8、比如有一个类叫汽车。你现在想在程序中买一辆,所以你用new来买了一辆汽车,这辆车你得给它找个名字, 比如宝马。 例如:汽车 宝马 = new 汽车(); 你有钱了,你要再来一辆: 汽车 奔驰 = new 汽车();
A4第四章第5节this解决成员变量和局部变量重名的问题
第四章第5节 1、当成员变量和局部变量(函数参数也可以看做局部变量)重名的时候,被看做局部变量,因此为了避免混乱,建议访问成员变量的时候加上“this.”,this代表当前对象。
2、new Person().sayHello();创建一个匿名对象,然后调用。输出结果是? new Person().sayHello(); 等价于 Person t=new Person(); t.sayHello(); 3、【类】里边定义的变量是成员变量,【方法】里边定义变量叫做局部变量
A4第四章第6节public和private
第四章第6节 1、我们可以把age成员变量声明为public,也可以把setAge声明为private,这样就只能在【类内部】调用private成员(再写一个方法调用private的setAge)
2、结论:public成员可以【被类内部或者外部】访问,private成员【只能被类内部】访问。这样可以保护不希望外接调用的内部成员(Member,包含字段Field/变量、方法)不被外界访问。
3、直接通过public的age设置年龄,不通过setAge赋值,这样有什么坏处?-1。【不能有效控制非法字符的赋值】
4、字段(Field)/成员变量(Member Variable)一般声明为private。通过get/set方法来进行取值/赋值。
A4第四章第7节属性入门
第四章第7节 1、为了避免外界给成员变量随便赋值,必须把成员变量声明为private,然后提供get/set方法,写起来、调用起来都麻烦,C#提供了“属性”这样一个语法:
private int height;//成员变量 public int Height//写成大写格式,方便外界调用属性值//如果改为private则只能在内部调用 { get { return this.height; } set { this.height = value; } } p.Height=50;//相当于调用【set方法对p的Height进行【赋值】 int a = p.Height;//相当于调用【get方法对p的Height进行【取值】
通过Reflector看属性本质上是什么?( ILSpy的C#看的优化后的代码,IL看的是IL代码,看不懂 )
public void set_Height(int value)//反编译中查看的代码set { this.height = value; } public int get_Height()//反编译中查看的代码get { return this.height; }
A4第四章第8节 属性的简化写法
第四章第8节 1、//如果是简单地set。get逻辑,暂时没有更多的逻辑代代码,可以简化如下; public string Name { get; set; } 2、Reflector反编译器 查看PropertyTest1.exe--{}propertyTest1-Name:string 发现显示的语法其实还是以前讲的定义的 public void getAge(int Age) { return this.Age; } public void setAge(int Age) { this.Age=Age; }
1、如果是简单set/get逻辑,可以使用更简写的语法: public int Weight { get; set; } 通过Reflector看本质是什么
2、get、set可以有一个声明为private、protected,这样就可以设置不同的访问级别。
3、如果只有get或者set就是只读或只写属性。只读只写【不能简化写】。
A4第四章第9节 构造函数入门和属性赋值的简化写法
第四章第9节 1、构造函数是创建类对象,并且在创建完成前对类进行初始化的特殊函数。如果定义类时没有声明构造函数,【默认】会给出一个无参构造函数,如果定义了任意一个构造函数,将【不会】提供默认的无参构造函数。
2、构造方法/函数格式及特点:
方法名【必须】和类名一致
【没有】返回值类型
构造函数可以重载,Person(String name,int age)
3、 class Person { public int Id { get; set; }//id属性 public string Name { get; set; }//Name属性 public int Age { get; set; }//Age属性 public Person()//默认的构造函数【也可以不写,但是通过反编译器会看到它是默认存在的】 { } public Person(int id)//构造id函数,给id进行初始化、、它的存在会使默认的构造函数失去。除非。你申明了默认构造函数 { this.Id = id; } } Person p = new Person() { Id=5,Name="abc"};//new出来一个新的对象,并且赋值id和Name Person p1 = new Person(5) { Name = "aaa", Age = 12};//Person后的括号中的5其实是初始化年龄为5,花括号内的Age又重新对new出的对象赋值了12 Person p2 = new Person { Id=5,Name="a"};//Person后的括号可以省去,但是最好不要省去
通过ILSpy反编译看一下,还是一个编译器玩的“语法糖”,所以和构造函数不一样,赋值是在对象构造之后
A4第四章第10节static介绍
第四章第10节 1、一些场景下会要求一个类的多个实例共享一个成员变量;有时候想定义一些不和具体对象关联、不需要new就调用的方法
举例:Console类的WriteLine,MessageBox的Show
2、static方法不需要new就可以直接通过类名调用。
3、static变量不需要new就可以直接通过类名调用。static变量是共享的内存空间,非static变量则是对象隔离的。
4、static 方法中无法使用this关键字,因为static独立于对象存在,不是任何人的唯一。
5、static成员中只能访问static成员,不能直接访问非static成员。非static成员可以访问static成员。
A4第四章第11节单例模式
第四章第11节 1、有的类在系统中只能有一个对象(*,资源管理器、缓存管理器等),这时就要使用“单例模式”(singleton)。实现单例模式有很多方法,先介绍最简单、最实用的“饿汉式”。
2、单例模式的实现步骤:
1)构造函数声明为private,这样避免外界访问
2)定义一个private readonly static的对象实例,static成员的初始化只在类第一次使用的时候执行一次。readonly修饰变量只能在构造函数或者初始化的时候赋值。
3)定义一个public static的getInstance方法,返回唯一实例。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SingletonTest1 { class God { //静态变量的初始化只执行一次 private static God Instance = new God(); private God() { }//构造函数声明为private,避免外界调用 public void ZhaoYaoDaDi() { Console.WriteLine("照耀"); } public static God GetInstance() { return Instance; } } }
A4第四章第12节静态代码块以及面试题
第四章第12节 class MyTest { static MyTest() { Console.WriteLine("zi 静态代码块"); } public MyTest() { Console.WriteLine("zi 构造方法"); } } MyTest t1 = new MyTest(); MyTest t2 = new MyTest(); 【静态代码块在类第一次被使用的时候执行一次,在构造函数执行之前执行。【只要用到类】,哪怕没new对象(比如只是声明变量)也会被执行,且只执行一次。
public class MyJingTaiDaimaKuai { public static int i = test1();//最开始执行 public int age = test2();//new的时候先执行成员变量的初始化,再执行构造函数 static MyJingTaiDaimaKuai() { Console.WriteLine("静态代码块");//再执行 } public MyJingTaiDaimaKuai() { Console.WriteLine("构造函数"); } public static int test1() { Console.WriteLine("test1"); return 3; } public static int test2() { Console.WriteLine("test2"); return 333; } } MyJingTaiDaimaKuai a1 = new MyJingTaiDaimaKuai(); Console.WriteLine(a1.age); MyJingTaiDaimaKuai a2 = new MyJingTaiDaimaKuai(); Console.WriteLine(a2.age);
【一般用于对类进行初始化,以后会用
只要一旦开始“接触一个类”,静态成员、static代码块就会被执行】
A4第四章第13节命名空间
第四章第13节 用文件系统的文件夹解释避免文件名重复的问题。
命名空间语法:
namespace ConsoleApplication1包住类;
当前命名空间中的类无需引用;使用using引用其他包中的类:
还可以直接通过“命名空间+类名”的方式使用“System.Data.SqlClient.SqlConnection”,不用using,适用于同时使用多个重名的类,比using时候的别名好;
A4第四章第14节继承基本语法
第四章第14节 C#中一个类可以“继承”自其他类,如果A继承自B,则A叫做B的子类,B叫做A的父类(基类)。子类会从父类继承所有非private成员。子类还可以有子类。
C#中一个类只能有一个父类,如果没指定父类,则System.Object为父类。
class FuLei { private void method1() { } public void method2() { } } class ZiLei : FuLei { private void method3() { } public void method4() { } } ZiLei zl1 = new ZiLei(); zl1.method1(); zl1.method2(); zl1.method4(); zl1.toString();
A4第四章第15节继承中的构造函数
第四章第15节 C#中一个类可以“继承”自其他类,如果A继承自B,则A叫做B的子类,B叫做A的父类(基类)。子类会从父类继承所有非private成员。子类还可以有子类。
C#中一个类只能有一个父类,如果没指定父类,则System.Object为父类。
子类的构造方法默认都去访问了父类的【无参构造方法】:在子类中的构造方法后都有一行默认语句 base()
class Fu { public Fu() { Console.WriteLine("fu"); } } class Zi : Fu { public Zi() : base()//不管是否显式调用,控制台都会输出fu { Console.WriteLine("zi"); } } Zi z = new Zi(); 先执行父类的构造函数把父类初始化完成,再初始化子类的。
可以通过super(参数)去访问父类中的有参构造函数。可以通过this(参数...)去访问本类中的其他构造函数。
class Fu { public Fu(int a) { Console.WriteLine("fu"+a); } } class Zi : Fu { public Zi():base(0)//为了避免出错,给父类传递一个整数0,来调用父类的有参构造函数 { Console.WriteLine("zi"); } public Zi(int a):base(a)//默认会调用父类的无参的构造函数。不过父类中没有无参构造函数,故将a传递给父类的构造函数 public Fu(int a) { Console.WriteLine("zi"+a); } } 如果定义了构造函数,则类就不会有默认的无参构造函数;如果父类中没有默认的,则子类构造函数必须显示调用父类的构造函数
一个类如果没有定义其他构造函数,则有一个【默认的无参构造函数】。构造函数会默认调用父类的【无参构造函数】 子类的无参或者是有参构造函数都会默认调用父类的【无参构造函数】
A4第四章第16节 private、public和protected的区别
第四章第16节 private成员无法被子类访问,子类只能通过父类的非private方法“间接”访问父类的private成员。这样保证了父类private成员的安全性。
procected成员只能被自己以及子类(直接或者间接)访问,无法被“外姓人”访问。
A4第四章第17节 Override
第四章第17节 面向对象三大特征:封装、继承、多态。多态是面向对象最强大的一个特征,也是最难的一个特征,设计模式等都是多态的体现。大型项目架构也大量应用多态。
子类中定义和父类中一样的方法就叫“重写(Override)或覆盖”,父类中可以被Override方法要声明为virtual 。Override和OverLoad区别见备注
class DiQiuRen { public virtual void speak(){Console.WriteLine("我是地球人");} } class Chinese : DiQiuRen { public override void speak(){Console.WriteLine("我是中国人");} public void baiNian(){Console.WriteLine("过年好!");} } DiQiuRen dqr1 = new DiQiuRen(); dqr1.speak(); Chinese zgr1 = new Chinese(); zgr1.speak();
面试常考: OverLoad:重载,方法的名字一样,参数的类数的个数或者类型不一样。 OverRide:重写,子类有和父类一样(名字,参数个数,类型,返回值)的方法(非private)
A4第四章第18节 多态的精髓
第四章第18节 1、不能用【子类的变量】指向【父类的对象】 例如:Chinese zgr2=new DiQiuRen();//地球人不一定是中国人 2、可以用【父类的变量】指向【子类的对象】 例如:DiQiuRen dqr2=new Chinese();//中国人一定是地球人 3、调用方法还是对象的方法,而不是变量类型的实现 4、记住::【【能够调用什么方法由变量类型决定,执行谁的方法由实际指向的对象决定】】 DiQiuRen dqr1=new Chinese(); DiQiuRen 是变量类型,,Chinese是指向的对象
下面的执行结果是什么?? Chinese zgr2=new DiQiuRen();//地球人不一定是中国人 DiQiuRen dqr2=new Chinese();//中国人一定是地球人 dqr2.sayHello();//实际上执行的是Chinese里的sayHello而不是DiQiuRen里的sayHello方法。。执行谁的方法由实际指向的对象决定
5、变量类型是“把对象看成什么”,DiQiuRen dqr2=new Chinese()是把Chinese对象看成是“地球人”,因为“地球人”不一定有baiNian方法,因此dqr2.baiNian()编译失败。
A4第四章第19节 多态与类型转换
第四章第19节 1、编译器只看类型,无法看实际是什么对象 2、ZGR zgr2=(ZGR)dqr2//显示转换..父类显示转换为子类 3、
下面程序的执行结果是什么?
Chinese zgr5 = (Chinese)dqr2;
zgr5.sayHello();
zgr5.baiNian();
Chinese zgr5 = (Chinese)dqr1;
()类型转换可以把“被看做父类对象的实例”重新看成“子类”。(显式类型转换/强制类型转换)
如果对象就是父类对象,当被看成子类对象的时候会失败,抛出运行期异常,编译器无法发现这个错误。
类型转换只能在有父子关系的类中进行
思考:
1、定义一个方法 void test1(DiQiuRen dqr){dqr.sayHello();}
如下调用test1(new Chinese());可以吗?运行结果是什么?
2、String s = (String)zgr1;可以吗?
A4第四章第20节抽象类
第四章第20节 1、把类标记为abstract,这样的类无法被直接实例化(new),这就叫抽象类。
2、DiQiuRen的sayHello输出“我是地球人”显然不合理,因为无法确定怎么说,也就是DiQiuRen不知道如何sayHello, 只有具体到中国人、日本人、美国人才知道如何sayHello
3、把DiQiuRen的sayHello的方法体去掉,并且方法增加abstract修饰,类也修饰为abstract:
abstract class DiQiuRen
{
public abstract void speak();//没有方法体的抽象方法
}
4、抽象方法没有方法体;一旦类中定义了抽象方法,类必须被修饰为抽象;抽象类无法实例化(new)。 5、抽象类中不一定有抽象方法
A4第四章第21节接口
第四章第21节接口 1、接口是一种用来声明“能力”的类型,不提供具体实现
语法:
public interface ISpeakable
{
void speak();
}
2、不提供实现方法,连{}都不能有。
3、接口无法实例化,只能被类“实现”
public class TeacherCang : ISpeakable
{
}
4、既可以使用接口类型变量又可以使用类类型变量调用speak
5、接口的意义是定义“做什么”,类定义“怎么做”
6、接口中不能声明变量(字段),一个没有实现代码的接口中声明变量没意义。
7、接口中可以定义多个方法,也可以不定义任何方法(* 标识接口)。
8、接口只是“能力”不是“实现”,因此不能也没必要定义构造函数。
9、类只能有一个父类,类可以实现多个接口。测试一下:Speakable、Walkable。
************************************************************************************** 接口和抽象类的区别和联系(常见面试题,自己先看看)
1、抽象类里面可以有非抽象方法 但接口里只能有抽象方法 声明方法的存在而不去实现它的类被叫做抽像类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的所有抽像方法提供实现,否则它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
2、接口(interface)是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对像上调用接口的方法。由于有抽像类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口