面向对象
1.面向过程
- 概念
围绕程序流程为核心,主要是一堆方法调来调去,是一种结构化程序设计。
- 缺陷
1.缺乏对数据的封装。 2.数据和操作是分离的。 2.面向对象
围绕对象为核心,操作对象。主要是对现实世界事物的抽象。
3.面向接口
在面向对象编程的基础之上抽取接口。
4.面向切面
5.面向约定
抽象数据类型
所谓的抽象数据类型就是将不同类型的数据放一起用来描述一种新的事物。
类
1.类的概念
类是一个模板,定义了一种抽象数据类型,用于描述一类抽象事物对象的属性和方法。比如狗类,代表了所有狗的状态和行为。
类有内部类,匿名内部类。
2.属性
描述一类抽象事物对象的特征状态(静态)。
3.方法
描述一类抽象事物对象的行为(动态)。
4.类的定义
修饰符 class 类名{ //属性和方法 }
5.注意事项
- 一个*.java文件中,可以有多个类,但是只能有一个public类。
public class Parent{ //... } class child{ //... }
- public修饰的类名必须跟*.java文件名一致(包括大小写)
对象
1.对象概念
对象是类的一个实例,在现实世界是看得见摸得着的,有具体的属性和方法。比如二哈是一个具体的对象,有颜色、名字等属性;有摇尾巴、叫等方法。
2.创建对象
- 语法
方式一: 类型名 引用名 = new 类型名(); 方式二: 类型名 引用名 = null; 引用名 = new 类型名();
new 类型名();会在堆空间创建对象开辟一个地址,然后将地址传给引用名;引用名在栈空间,引用名就通过这个地址来对对象进行操作。
- 默认值
对象创建后,若未进行赋值,则对象会有默认值:
byte, short, int, long 默认值为:0 flaot, double 默认值为:0.0 char 默认值为:'\u0000'(空白字符) boolean 默认值为:false 引用类型 默认值为:null
类和对象的区别和联系
1.现实世界是由很多对象组成的,并且对象之间存在联系。 2.现实世界是先有对象,然后再抽类。
代码世界是先有类,然后再创建对象。
3.一个类可以有很多对象,同一个类的多个对象可以结构相同、数据不同。
属性
属性就是对象的特征状态
方法
方法就是对象的行为。
1.方法的概念
- 方法是用来封装一段特定的逻辑功能,尽可能单一(一个方法只做一件事)。
- 方法可以被反复的调用。
- 方法可以减少代码的重复冗余,便于程序的维护。
2.方法的定义
- 方法定义五要素:修饰符、返回类型、方法名、参数列表、方法体。
public static void method(int x){ //方法体 }
3.参数
- 形参
在定义方法时声明的参数就是形参。
- 实参
在调用方法时传入的参数就是实参。
4.返回类型
- 方法有结果
方法返回类型为结果类型,通过return关键字返回。
- 方法无结果
方法返回类型为void
5.方法签名
方法签名包含两个方面:方法名和参数列表。
6.注意事项
- 一个类中,不能存在两个或多个方法签名一样的方法,即方法的方法名和参数列表不能同时相同。
- 方法名可以相同,但是参数列表不能相同(参数个数、参数类型、参数顺序不能相同)。
7.可变长参数
- 概念
只能在定义方法参数时使用。可以描述多个相同类型的参数,也可以表示该类型数组类型的参数。
- 定义
public void test(int... a){ //... }
- 注意事项
1.优先调用精确匹配的方法。
//方法1 void f(int a){ //... } //方法2 void f(int... a){ //... } 调用时会调用方法1
2.可变长参数只能出现在方法参数列表的最后位置,因此,一个方法只能拥有一个可变长参数。
构造方法
1.作用
用于创建对象,完成对象创建的初始化(给对象的成员变量赋值)。在Java中,使用关键字new来创建一个新的对象。创建对象需要以下三步:
1.声明 声明一个对象,包括对象名称和对象类型。 2.实例化 使用关键字new 来创建一个对象。 3.初始化 使用new创建对象时,会调用构造方法初始化对象。 2.语法
不能定义返回类型,方法名必须跟类名一致。
public class Test{ int a; //构造方法1 Test(){ //... } //构造方法2 Test(int a){ this.a = a; //... } }
3.注意事项
1. 如果没有显式的为类定义构造方法,Java编译器会自动为该类提供一个默认的无参构造方法;
2.如果显式的为一个类提供一个或多个有参构造方法,系统则不会提供默认的无参构造方法,因此,当父类中定义了一个或多个有参数构造方法时,应显式的写一个无参数的构造方法,以防调用无参构造方法和子类省略super关键字时出现错误。 3.构造方法不能定义返回类型,方法名要跟类名一致。 4.构造方法的重载
可以对一个类定义多个构造方法,在创建对象时,Java编译器会根据不同参数调用不同的构造方法。
访问实例变量和方法
通过已创建的对象来访问成员变量和成员方法。
继承
1.继承的概念
继承就是子类继承父类的特征状态和行为,使得子类对象(实例)具有父类的特征状态和方法。所以,继承需要符合的关系就是:is-a关系,父类更通用,子类更具体。
继承使用场景:①父类功能不够,需要一个子类去扩充原有的功能。②很多类具有相同的方法,然后将这些共有的东西抽取封装成一个父类,然后子类去继承即可。
继承描述如下图所示:
2.注意事项
Java不支持多继承,但是支持多重继承。如下图所示:
3.继承的特性
1.子类继承父类后,具有且可见父类的非private属性和方法,具有且不可见父类的private属性和方法 2.子类可以拥有自己独特的属性和方法,即子类可以对父类进行扩展。 3.子类可以重写父类的方法 4.继承提高了类与类之间的耦合性(即继承的缺点,耦合度高会造成代码之间的联系越紧密,代码的独立性越差)。 5.继承会减少所有子类中重复性代码(提高复用性),维护性更高,代码更加简洁。 6.一个子类只能拥有一个父类,而一个父类可以拥有多个子类,即子类的单一继承。 7.如果一个类没有继承另一个类,那么Java会默认为这个类继承Object类。即Object类是所有类的父类。(这个类在java.lang包中,不需要import) 4.继承关键字
继承可以使用extends和implements这两个关键字来实现继承,而且所有的类都是继承自java.lang.object。
- extends关键字
只能单一继承,一个类只能拥有一个父类。
- implements关键字
用于继承接口,可同时继承多个接口(接口与接口之间采用逗号进行分割)。
public interface A { public void eat(); public void sleep(); } public interface B { public void show(); } //继承多个接口 public class C implements A,B { }
5.final关键字
final关键字声明类可以把类定义位不能被继承的,即最终类;或者由于修饰方法,则该方法不能被子类重写。
子类
1.构造子类对象
1. 构造子类对象时,会先构造所有父类对象,最后构造子类对象。 2.子类不会继承父类的构造方法,但会继承父类的私有属性和方法,但私有属性和方法对子类不可见。 3.如果子类有构造方法,那么创建子类的对象时,先执行父类的构造方法,再执行子类的构造方法。 4.如果子类没有构造方法,则它默认调用父类无参的构造方法,如果父类中没有无参数的构造方法,则将产生错误。 5.按继承关系,构造方法是从顶向下进行调用的。定义子类的一个对象时,会先调用子类的构造函数,然后在调用父类的构造函数,如果父类函数足够多的话,会一直调用到最终的父类构造函数,函数调用时会使用栈空间,所以按照入栈的顺序,最先进入的是子类的构造函数,然后才是邻近的父类构造函数,最后再栈顶的是最终的父类构造函数,构造函数执行是则按照从栈顶到栈底的顺序依次执行. 2.super关键字
super有两种形式:super和super(),指向父类的引用。
- super
用于调用父类的成员变量名和方法。使用super首先到直接父类查找匹配,如果未找到,再逐层向上到祖先类查找。
//祖先类 public class Root { public void test() { System.out.println("root test "); } } //父类 public class Parent extends Root { } //子类 public class Child extends Parent{ @Override public void test() { System.out.println("child test "); } public void sum() { super.test(); } } //测试类 public class Test { public static void main(String[] args) { Child child = new Child(); child.sum(); } } 输出结果为:root test
格式如下:
super.<父类的成员变量>; super.<父类的成员方法名>;
- super()
通过super()来调用父类中定义的构造方法(可用于在初始化对象时使用)。格式如下:
super();或者super(参数);
1. super关键字是指向父类的引用。 2.若子类的构造方法没有调用父类的构造方法或者使用this(),那么编译器会自动为子类构造方法添加一个默认的super()方法。 3.使用super()时,如果父类没有无参构造方法时,编译器就会报错。 4.super()语句必须在构造方法的第一行。
this关键字
this关键字指向本类的引用。
- this关键字只能用于方法体中。
- 表示当前对象,即哪个对象调用指向那个对象。
- this(参数列表)只能放在构造方法第一句位置,表示调用当前类的构造方法;super(参数列表)同样放在第一句位置,表示调用父类的构造方法;
- this.xxx与super.xxx同理调用属性和方法。
- this(参数列表)与super(参数列表)不能同时出现。
多态
1.多态概念
多态是指一个行为具有多个不同表现形式或者形态的能力。多态就是多种形态。多态就是一个接口,使用不同的实例而执行不同的操作。如下图所示:
如上图所示,打印机可以有彩色打印机和黑白打印机。
2.向上造型(小到大的转换)
- 概念
将子类对象提升为父类类型看待 (即父类对象的引用指向子类对象)。
- 注意事项
一个类的对象可以向上造型的类型有:父类的类型;实现的类型(接口)。
Java编译器根据类型类型检查调用的方法是否匹配。
- 实质
一个子类的对象可以向上造型为父类的类型,即定义父类的引用指向子类的对象。但是通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分。如下所示:
class Parent{ void print(){} } class Child extends Parent{ //重写方法 void print(){} //自定义方法 void show(){} } 如下所示: Parent p = new Child(); p.print();//是正确的,运行调用子类的print()方法 p.show();//是错误的,会报错。父类没有show()方法
3.强制转型(大到小的转换)
- 可以通过强制转换将父类型变量转换为子类型变量,前提是该变量指向的对象确实是该子类类型。
- 可以通过强制转换将变量转换为某种接口类型,前提是该变量指向的对象实现了该接口。
- 如果在强制转换过程中出现违背上述两个前提,程序将会抛出ClassCastException异常。
- Java编译器默认为父类型大,子类型小。
4.多态存在的必要条件
- 继承
- 重写
- 父类的引用指向子类的对象
5.多态的实现方式
- 重写
- 接口
- 抽象类与抽象方法
6.注意事项
使用多态方式调用子类的方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,则再去调用子类的同名方法。即编译看父类,运行看子类。
7.多态的优点
1.消除类型之间的耦合关系 2.可替换性 3.可扩展性 4.接口性 5.灵活性 6.简化性 7.屏蔽不同子类之间的差异性。 8.多态意义
- 一个类型的引用在指向不同对象时可以有不同的实现(方法)
- 同一个对象,在造型成不同的类型时,会有不同的功能表现(方法)
instanceof关键字
参考Java基础概述运算符
重写(Override)
1.重写概念
重写就是子类对父类的允许访问的方法的实现过程进行重新编写(重写只针对方法),返回值和形参都不能改变(即外壳不变,核心重写)。重写的好处就是子类可以定义自己独特的行为。如下所示:
//动物类 class Animal{ public void move(){ System.out.println("动物可以移动"); } } //狗类 class Dog extends Animal{ public void move(){ System.out.println("狗可以跑和走"); } } //测试类 public class TestDog{ public static void main(String args[]){ Animal a = new Animal(); // Animal 对象 Animal b = new Dog(); // Dog 对象 a.move();// 执行 Animal 类的方法 b.move();//执行 Dog 类的方法 } } 运行结果: 动物可以移动 狗可以跑和走
在上面的例子中可以看到,尽管b属于Animal类型,但是它运行的是Dog类的move方法。这是由于在编译阶段,只是检查参数的引用类型。然而在运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法。因此在上面的例子中,之所以能编译成功,是因为Animal类中存在move方法,然而运行时,运行的是特定对象的方法。
2.重写的规则
1.方法名和参数列表必须完全与父类被重写方法的相同。 2.返回类型与父类被重写方法返回值类型可以不相同,但是必须是父类返回值类型的子类(在JDK1.5及以后)。 3.子类方法访问修饰符权限不能比父类中被重写的方法的访问修饰符权限更低(要大于等于)。 4.子类方法抛出的异常不能比父类被重写的方法抛出的异常大。 5.父类的成员方法只能被它的子类重写。 6.声明为final的方法不能被重写。 7.声明为static的方法不能被重写,但是能够被再次声明。 8.子类和父类在同一个包中,那么子类可以重写父类的所有方法,除了声明为private和final方法。 9.子类和父类不再同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。 10.构造方法不能被重写。 11.如果不能继承一个方法,则不能重写这个方法。 3.注意事项
- 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛更加大。例如,父类一个方法声明了一个检查异常IOException,但是在重写这个方法时不能抛出Exception异常,因为Exception是IOException的父类。
- 子类在实例化后调用重写方法时,首先会调用子类的重写方法,如果子类没有重写该方法,则会向上调用父类的该方法,如果父类没有重写这个方法,则会再去调用上一级的该方法,直到找到该方法。编译看父类,运行看子类。如下所示:
//祖先类 public class Root { public void test() { System.out.println("root test "); } } //父类 public class Parent extends Root { @Override public void test() { System.out.println("parent test "); } } //子类 public class Child extends Parent{ @Override public void test() { System.out.println("child test "); } } //测试类 public class Test { public static void main(String[] args) { Child child = new Child(); child.test(); } } 输出结果为:child test
有如下测试:
1.如果子类没有重写test方法,则输出结果为:parent test 2.如果子类重写了test方法,但是方法里面没有任何实现,则输出结果为:什么都不输出 3.如果Child和Parent都没有重写test方法,则输出结果为:root test
- 在子类重写方法中,可以使用super关键字来调用父类方法。
重载(Overload)
1.重载概念
重载就是在一个类里面,方法名称相同,而参数不同,返回类型可以相同也可以不相同。如下所示:
public class Overloading { public void test(){ System.out.println("test1"); } public int test(int a){ System.out.println("test2"); return a; } }
2.重载规则
1.被重载的方法必须改变参数列表(参数个数、参数类型、参数顺序不能相同)。 2.被重载的方法可以改变返回类型。 3.被重载的方法可以改变访问修饰符。 4.被重载的方法可以声明新的或者更广的检查异常。 5.方法能够在同一个类中或者在一个子类中被重载。 6.重载的方法是不同的方法,只是方法名一样。 3.重载与重写的区别
区别点 重载方法 重写方法 参数列表 必须修改 不能修改 返回类型 可以修改 不能修改 异常 可以修改 可以减少或者删除,不能抛出新的或者更广的异常 访问 可以修改 不能做更严格的限制(可以降低限制) 编译时看重载,运行时看重写。 class Parent{ public void show(){ System.out.println("Parent.show()"); } } class Child extends Parent{ public void show(){ System.out.println("Child.show()"); } } class Go{ public void g(Parent obj){ System.out.println("g(Parent)"); obj.show(); } public void g(Child obj){ System.out.println("g(Child)"); obj.show(); } } class Test{ public static void main(String[] args){ Parent p = new Child(); Go g = new Go(); g.g(p); } } 运行结果为: g(Parent) Child.show()
如下图所示:
4.总结
方法重载是一个类的行为多态性表现,而方法重写是子类与父类的一种多态性表现。
封装
概念
通过类型修饰符实现对类里面的一些东西进行私有化封装,外部不能直接操作,只能通过类对外提供方法进行间接操作。
接口(interface)
1.接口概念
接口是一种标准、规范,是一个抽象类型,是抽象方法的集合(比抽象类更加抽象)。接口使用interface修饰,一个子类通过implements关键字来实现接口的方式,从而来实现接口的抽象方法。
2.接口语法
- 声明接口
声明接口关键字使用interface,如下所示:
interface P{ //代码 }
- 接口中定义的成员变量全部都是公开的常量
即成员变量是public static final修饰的,如果没有显示的添加,Java编译器会默认添加。
interface P{ public static final int NUM = 1; int sum = 2; //java编译器会默认为sum添加public static final }
- 接口中定义的成员方法全部是公开的抽象方法
即成员方法是public abstract修饰的,如果没有显示的添加,Java编译器会默认添加。
interface P{ public abstract void num(); void sum(); //java编译器会默认为sum添加public abstract }
3.类实现接口
一个实现类通过使用关键字implements来实现接口,与继承不同,一个实现类可以实现多个接口,使用逗号","分隔开。该类就必须实现这些接口的所有方法。
interface Root{ public abstract void r(); } interface Parent{ public abstract void p(); } //实现接口 class Child implements Root, Parent{ public void r(){ //代码 } public void r(){ //代码 } }
4.注意事项
- 接口类型的引用可以指向实现了该接口的类的对象
interface Parent{ void p(); } public class Child implements Parent{ public void p(){ //代码 } public static void main(String[] args){ Parent p = new Child();//接口类型的引用可以指向实现了该接口的类的对象 p.p();//编译看父类(接口),运行看子类(实现类) } }
5.接口继承接口
接口与接口之间可以存在继承关系,使用extends来实现继承。由于接口不是类,所以一个子接口可以继承多个父接口,使用逗号分隔。
interface Root{ } interface Parent{ } //接口继承接口 interface Child extends Root, Parent{ }
6.类与接口的区别
1.接口不是类。类描述对象的属性和方法,而接口则包含类要实现的方法(除非是抽象类)。 2.接口不能用于直接实例化对象。 3.接口没有构造方法 4.接口里面所有的方法都是抽象方法 5.一个类只能继承单一类,但是可以实现多接口 6.如果一个类同时要继承类和实现接口,必须先继承再实现接口,否则报错 第6点如下所示:
interface Root{ } class Parent{ } //下面代码是正确的 class Child extends Parent implements Root{ } //下面代码是错误的 class Child implements Root extends Parent{ }
7.抽象类与接口的区别
1.抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。 2.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。 3.接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
内部类
1.概念
一个类可以定义在另外一个类的内部,定义在类的内部的类称之为内部类,其所在的类称为外部类。
内部类定义在外部类的内部,通常用于服务外部类。对外部类之外的类不具备可见性,内部类可以直接调用外部类的成员。
2.内部类分类
内部类分类为:成员内部类、静态内部类、方法内部类、匿名内部类。
3.内部类优点
1.内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。 2.内部类是另外一种封装,对外部类之外的其他类隐藏。 3.内部类可以实现java的单继承局限。 4.内部类与外部类的关系
1.内部类可以直接访问外部类的元素(包括私有域) 2.外部类可以通过内部类的引用间接访问内部类元素 3.内部类是一个相对独立的个体,与外部类没有关系 5.创建内部类对象
一般情况下,内部类对象会在外部类对象中创建(构造方法或者其他方法中)。内部类对象中会有一个隐式的引用指向创建它的外部类对象(非静态内部类)。
- 在外部类外部创建非静态内部类
语法:外部类.内部类 内部类对象 = new 外部类().new 内部类(); 如下:Outer.Inner in = new Outer().new Inner();
- 在外部类外部创建静态内部类
语法:外部类.内部类 内部类对象 = new 外部类.内部类(); 如下:Outer.Inner in = new Outer.Inner();
- 在外部类内部创建内部类
在外部类内部创建内部类就跟创建普通类对象一样: 内部类 内部类对象 = new 内部类();
6.成员内部类
成员内部类相当于外部类的一个成员存在,其内部不允许存在任何static变量或者方法,正如成员方法中不能存在任何静态属性(成员方法与对象有关,静态属性与类有关)。
class Outer { //成员内部类 class Inner{ } }
7.静态内部类
使用static修饰的内部类称为静态内部类。与非静态内部类最大的不同在于:非静态内部类在编译完成后会隐含的保存着一个引用,该引用指向创建它的外部类对象;但是静态类没有这个引用,这就意味着:
1.静态内部类的创建不需要依赖外部类就可以直接创建 2.静态内部类不可以使用任何外部类的非static成员(包括属性和方法)。 3.静态内部类可以有自己的静态成员 如下所示:
public class Outer { public int num = 2; //静态内部类 static class Inner{ //静态属性 public static int x = 2; void show() { //System.out.println(num); //错误,静态内部类不能引用外部类非静态属性 } } public static void main(String[] args) { //创建静态内部类对象 Outer.Inner inner = new Outer.Inner(); } }
8.方法内部类
方法内部类就是定义在方法里面的类。有如下规则:
1.方法内部不允许使用访问修饰权限符(public,private,protected) 2.方法内部类对外部完全隐藏,除了创建这个类的方法可以访问它。 3.方法内部类如果想要使用方法形参,该形参必须使用final声明(JDK8形参变为隐式final声明) 定义如下所示:
class Outer{ private int num =5; public void dispaly(final int temp){ //方法内部类即嵌套在方法里面 class Inner{ } } }
9.匿名内部类
- 概念
若只要一个当前类的子类对象,而该子类对象创建之后,这个子类的价值也就不存在了,那么这个类不必命名,称之为匿名内部类。匿名内部类就是一个没有类名的方法内部类,因此特点和方法跟方法内部类一致,除此之外,还有自己的特点:
1.匿名内部类必须继承一个抽象类或者实现一个接口 2.内部类没有类名,因此就没有构造方法
- 好处
不需要创建子类和子类对象,可以减少子类的个数。
- 使用
当只需要一个当前类子类对象,而且该子类对象使用完就没用了。
public class Test3 { public static void main(String[] args) { Outer outer = new Outer(); outer.show(); } } //接口 interface MyInterface{ void test(); } //外部类 class Outer{ public void show() { //创建匿名内部类,实现了MyInterface接口 new MyInterface() { @Override public void test() { System.out.println("匿名内部类"); } }.test();//调用test方法 } } 运行结果:匿名内部类
可以看到,实际上MyInterface是一个接口,本身接口是不能直接new的,但是发现在new MyInterface()的时候,后面带了一对花括号,那么这个就表示new的是这个接口的实现类。这个实现类是没有名字的。在创建内部类后调用了test()方法。
参考资料: