1.面向对象的三大特性——封装,继承,多态
封装和继承目的都是为了代码重用,多态目的是为了接口重用。
2.封装
1.概念:
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
2.好处:
1.只能通过规定的方法访问数据;
2.隐藏类的实例细节,方便修改和实现
3.实现步骤:
注意:对封装的属性不一定要通过get/set方法,其他方法也可以对封装的属性进行操作。当然最好使用get/set方法,比较标准
4.访问修饰符:
从上到下封装性越来越差,如图所示:
5.this关键字
1.this关键字代表当前对象
this.属性 操作当前对象的属性
this.方法 调用当前对象的方法。
2.封装对象的属性的时候,经常会使用this关键字。
3.当getter和setter函数参数名和成员函数名重合的时候,可以使用this区别,例如:
6.内部类
内部类( Inner Class )就是定义在另外一个类里面的类,与之对应,包含内部类的类被称为外部类。
内部类的主要作用:
1.内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
2.内部类的方法可以直接访问外部类的所有数据,包括私有的数据。
3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。
分类:
-
成员内部类
内部类中最常见的就是成员内部类,也称为普通内部类
使用方法:
那么外部类如何使用内部类的成员和方法呢?
1.可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。
Java 编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部 类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。
2、 如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。如:
-
静态内部类
静态内部类是 static 修饰的内部类,这种内部类的特点是:
1、 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问。
2、 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“**成员名”**直接调用外部类的静态成员。
3、 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
-
方法内部类
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。
-
匿名内部类
匿名类是不能有名称的类,所以没办法引用他们。必须在创建时,作为new语句的一部分来声明他们。
但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。
这就要采用另一种形式 的new语句,如下所示:new <类或接口> <类的主体>
这种形式的new语句声明一个 新的匿名类,他对一个给定的类**进行扩展,或实现一个给定的接口。**他还创建那个类的一个新实例,并把他作为语句的结果而返回。要扩展的类和要实现的接口是 new语句的操作数,后跟匿名类的主体。
注意**匿名类的声明是在编译时进行的,实例化在运行时进行。**这意味着 for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
从技术上说,匿名类可被视为非静态的内 部类,所以他们具备和方法内部声明的非静态内部类相同的权限和限制。
假如要执行的任务需要一个对象,但却不值得创建全新的对象(原因可能 是所需的类过于简单,或是由于他只在一个方法内部使用),匿名类就显得很有用。匿名类尤其适合在Swing应用程式中快速创建事件处理程式。以下是一个匿名内部类的实例:
1.匿名内部类的基本实现:
可以看到,我们直接将抽象类Person中的方法在大括号中实现了,这样便可以省略一个类的书写,并且,匿名内部类还能用于接口上。
2、在接口上使用匿名内部类:
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现。
在使用匿名内部类的过程中,我们需要注意如下几点:
**1、**使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
**2、**匿名内部类中是不能定义构造函数的。
**3、**匿名内部类中不能存在任何的静态成员变量和静态方法。
**4、**匿名内部类为局部内部类(即方法内部类),所以局部内部类的所有限制同样对匿名内部类生效。
**5、**匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为 **final **。我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
3.继承
1.概念:
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,
被继承的类称为“基类”、“父类”或“超类”,
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“**继承”(Inheritance)和“组合”(Composition)**来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
2.实现方式:
实现继承、接口继承和可视继承。
实现继承是指使用基类的属性和方法而无需额外编码的能力;
接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,使用关键字 Interface 。
3.继承与接口与抽象类:
1.接口可以继承接口,使用extends 而不是implement。
2.接口不能继承抽象类,抽象类可以实现(implement)接口。
原因是:接口的实现和抽象类的继承都要重写父类的抽象方法。而接口里只能有抽象方法,抽象类里则允许有抽象方法和非抽象方法。
3.抽象类可以继承实体类。
4.继承的优缺点:
继承的优点:
a、提高代码的复用性
b、提高代码的维护性
c、让类与类之间产生关系,是多态的前提
继承的缺点:
增强了类与类之间的耦合性
4.多态
1.概念:
多态(Polymorphism)按字面的意思就是"多种状态"。在面向对象语言中,接口的多种不同的实现方式即为多态。
通俗的讲,就是同一个东西表现出多种状态,在面向对象的描述中就是同一个函数接口,实现多种不同的表现方式。
2.多态的条件:
(1)虚函数的重写(子类定义一个与父类的完全相同的虚函数)
(2)父类的指针或引用
当使用父类的指针或引用调用这个重写的虚函数时,指针指向父类就调用父类的虚函数,指向子类就调用子类的虚函数。
注意:
(1)基类(父类)必须为虚函数,派生类(子类)保持这一特性。(相当于父类必须为虚函数,子类可为虚函数也可不为虚函数)。
(2)除协变外,派生类(子类)的重写必须保持函数名、参数、返回值与基类(父类)保持相同。
(3)协变就是派生类的中函数的返回值可以与基类的不同,但返回值必须是指向父子关系的指针或引用。
(4)静态成员函数不能为虚函数。因为静态成员函数就相当于是受命名空间限制的普通成员函数,可以把它看做是一个类。它与类的实例无关,在调用时不会调用隐含的this指针,因此不能为虚函数。简言之,成员函数实例相关,静态函数类相关,虚函数相当于成员函数,所以静态函数不能为虚函数。
(5)在类外定义虚函数,只能在声明时加virtual,不能在类外定义时加virtual。
(6)构造函数、拷贝构造函数、赋值运算符重载不能是虚函数。
(7)析构函数要定义成虚函数------>保证正确调用对应的函数 。
正常情况下,子类对象在构造时先调用父类的构造函数,再调用子类的构造函数;析构时先调用子类的析构函数,再调用父类的析构函数。可以理解为子类有两部分,一部分是从父类继承的,一部分是自己定义的,初始化的时候先调用父类初始化从父类中继承的,再调用子类初始化自己定义的。析构函数也是一样,子类的析构函数只会析构自己定义的一部分,若父类不定义成虚函数,在delete时只会调用父类的析构而不调用子类的析构,最终会导致内存泄漏的问题。
3.抽象类和抽象方法:
当父类的某些方法不确定时,可以用abstract关键字来修饰该方法[抽象方法],用abstract来修饰该类[抽象类]。
抽象类是为了把相同的但不确定的东西的提取出来,为了以后的重用。
定义成抽象类的目的,就是为了在子类中实现抽象类。
(1)abstract关键字:
abstract关键字用来修饰抽象类和抽象方法。
(2)抽象类:
1.使用abstract关键字修饰的抽象类不能被实例化。
2.抽象类中可以有非抽象方法。
3.抽象类不能是密封的或者静态的。
(3)抽象方法:
1.使用abstract关键字修饰的抽象方法是一个没有实现的方法,由子类重写抽象方法来实现。
2.抽象方法没有大括号,直接在小括号后以分号";"结尾。
3.含有抽象方法的类必须是抽象类。
4.抽象方法必须在其子类中实现,除非它的子类也是抽象类。
4.为什么要使用多态?
1.增加程序的灵活性
2.增加代码的可扩展性
5.对象与内存分析
1.new 关键字表示创建一个对象
2.new 关键字表示实例化对象
3.new 关键字表示申请内存空间
4.如果使用一个没有申请内存空间的对象,会报空指针异常:java.lang.NullPointerException
注意:
(1)new关键字:表示向内存申请空间,也表示实例化一个对象,创建一个对象。
(2)一个对象在内存中的大小,由该对象的所有属性所占的内存大小的总和。引用类型变量在32位系统上占4个字节,在64位系统上占8个字节。加上而外的对象隐性数据所占的大小。
(3)相同的类型才可以赋值
(4)不同的引用,指向同一个对象,任何一个引用改变对象的值,其它引用都会反映出来。
(5)编程时要注意的问题,在确定不使用对象时,要尽早释放对象:引用=null
(6)当一个堆中的对象没有被任何引用变量所指向时,该对象会被JVM 的 GC 程序认为是垃圾对象,从而被回收。
6.构造方法
(1)构造方法名称与类名相同,没有返回值声明(包括 void)
(2)构造方法用于初始化数据(属性)
(3)每一个类中都会有一个默认的无参的构造方法
(4)如果类中有显示的构造方法,那么默认构造方法将无效
(5)如果有显示的构造方法,还想保留默认构造 方法,需要显示的写出来。
(6)构造方法可以有多个,但参数不一样,称为构造方法的重载
(7)在构造方法中调用另一个构造方法,使用this(…),该句代码必须在第一句。
(8)构造方法之间的调用,必须要有出口。
(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留。
(10)一个好的编程习惯是要保留默认的构造方法。(为了方便一些框架代码使用反射来创建对象)
(11)private Dog(){},构造方法私有化,当我们的需求是为了 保正该类只有一个对象时(单例模式就是私有化构造器)。
7.成员变量和局部变量的区别
8.值传递与引用传递
首先,注意:在java中只有按值传递,并没有所谓的按引用传递。
java数据类型可以分为两大类:基本类型(primitive types)和引用类型(reference types),两种类型都是将外面的参数变量拷贝一份到局部变量中,基本类型为值拷贝,引用类型就是将引用地址拷贝一份
总结:
1.当方法参数为基本类型时,是将外部变量值拷贝到局部变量中而进行逻辑处理的,所以方法是不能修改原基本变量的。
2.当传入参数为引用类型时,为对象的引用地址拷贝,但是引用拷贝还是没有更改原来的包装类型的变量值。(Java中的自动装箱机制)
3.对于引用类型的方法参数,会将外部变量的引用地址,复制一份到方法的局部变量中,两个地址指向同一个对象。所以如果通过操作副本引用的值,修改了引用地址的对象,此时方法以外的引用此地址对象也会被修改。(两个引用,同一个地址,任何修改行为2个引用同时生效)。
9.static关键字
10.方法的重写和重载
1.方法的重写:
(1)概念:
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
(2)方法重写的规则:
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
(3)Super关键字:
super关键字指代父类对象,主要用于在子类中指定父类的方法和属性,也用于在子类中初始化父类, 当需要在子类中调用父类的被重写方法时,要使用 super 关键字。子类的静态方法中不能使用super关键字。
super关键字的用法:
super可以用来引用直接父类的实例变量。
super可以用来调用直接父类方法。
super()可以用于调用直接父类构造函数
2.方法的重载:
(1)概念:
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
重载是方法名不变,但参数一定要变。而且重载的方法一般都写在一个类中。
(2)方法重载的规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
3.方法的重载和重写的区别:
4.总结:
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
- (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
- (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
- (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
11.final关键字
1.作用:
(1)修饰类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
(2)修饰方法
如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。注意:类的private方法会隐式地被指定为final方法。
(3)修饰变量
修饰变量是final用得最多的地方,对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
12.接口
1.语法:
2.接口的使用规则:
(1)定义一个接口,使用interface关键字
(2)在一个接口中,只能定义常量、抽象方法,JDK1.8后可以定义默认的实现方法
(3)接口可以继承多个接口:extends xxx,xxx
(4)一个具体类实现接口使用implements关键字
(5)一个类可以实现多个接口
(6)抽象类实现接口可以不实现接口的方法
(7)在接口中定义的方法没有声明 访问修饰符,默认为public
(8)接口不能有构造方法
(9)接口不能被实例化
java8新增:
(1)增加了default方法和static方法,这两种方法完全可以有方法体
(2)default方法属于实例,static方法属于类(接口)
(3)接口中的静态方法不会被继承,接口中的静态变量会被继承