Copyright @BJTShang
面向对象基础
1.面向对象概述
- 面向对象三个特征:封装(Encapsulation),继承(Inheritance),多态(Polymorphism)。(都是什么样的表现形式?)
- 开发,其实就是找对象,建立对象,使用对象,并维护对象之间的关系
- 类和对象的关系:
- 类:对事物的描述;属性和行为共同组成了类的成员(成员变量和成员方法,Java中方法就是函数)
- 对象:java在堆内存中使用new操作符建立的实体;存储对象的属性和行为;
2. 封装
- set() get()方法: 使得可以访问类内部的私有化(private)成员属性;参考:http://blog.youkuaiyun.com/shenqidemao/article/details/50699657
- 首先:为何要私有化数据?提高安全性,使得外部不能够直接访问,也是封装的一种表现形式;
- 其次,为何又要设置方法来使用数据(使用数据的两种方式:设置:set(),获取:get())?可以在方法中对数据的使用制定规则(逻辑判断语句)!
- 从而提高代码的健壮性,安全性。将不需要对外提供的内容(通常是属性)隐藏,提供公共(public)方法对齐访问;
3.构造方法
特点
- 与类同名;
- 没有返回值,也就不能写return;
- 没有定义构造方法时,系统默认加入一个空内容空参数的构造方法;但是,定义了构造方法后,默认的构造方法消失;
作用
- 对象初始化;
与一般方法的区别
- 写法不同;
- 对象建立的同时运行构造方法,初始化对象,而一般方法必须使用对象引用调用,给对象添加初始化以外的功能;
- 构造函数只在对象建立时运行一次,而一般方法可以多次被调用运行;
构造代码块
- 和构造方法相同:作用也是初始化对象,对象一建立就运行;但是优先于构造方法运行;
- 与构造方法的区别:对所有对象统一初始化,没有构造方法的重载功能;
4. this关键字
看上去:是用于区分局部变量和成员变量同名的情况;为何可以解决这个问题?
class Person{
Person(String name){
this.name = name;
}
public static void main(String[] agrs){
new Person("BJTShang");
}
}
this 代表本类对象,更具体:代表本方法所属类的对象的引用;
this关键字的使用
- 当定义类中方法时,该方法内部要用到该方法的对象时,用this表示这个对象,例如:对比两个人的年龄,相同返回true,不同返回false;
class Person{
private int age;
Person(int age){
this.age = age;
}
public boolean compare(Person p){
return this.age==p.age;//这里this代表使用这个方法的p1对象引用,局部变量p代表传入的p2对象引用
}
public void main(String[] agrs){
Person p1 = new Person(20);
Person p2 = new Person(25);
System.out.println(p1.compare(p2));
}
}
this的一个特殊应用:构造方法的相互调用,且必须放在构造方法的第一行(防止该构造方法后续的语句修改调用的构造函数中的初始化参数)
class Person{
String name;
private age;
Person(){
}
Person(String name){
this.name = name;
}
Person(String name,int age){
this(name);//相当于p1(name)调用同类的另一个构造方法
this();//调用空参数的构造方法
this.age = age;
}
public static void main(String agrs){
Person p1 = new Person("BJTShang",25);
}
}
另外,this还可以作为方法的返回值使用,典型应用在get()方法中。
5.对象的初始化
Person p1 = new Person("BJTShang",20);
这句对象初始化语句在内存中的过程(不考虑在方法区中的数据):
* 因为new用到了Person.class,所以会找到Person.class文件加载在内存中;
* 如果有的话,执行类中的static{}静态代码块,对类进行初始化;
* 在堆内存中开辟空间,分配内存地址;
* 在对内存中建立对象的特有属性(未被static修饰的,被static修饰的属性放入方法区/共享区/数据区),并进行默认初始化,例如String name=null;int age=0;
* 对属性进行显式初始化;例如在类中已经定义的String name=”BJTShang”;
* 对对象进行{}构造代码块初始化;
* 对对象进行对应的构造方法初始化;
* 将内存地址赋值给栈内存中的引用变量p1;
6.单例设计模式(重要):
一个类在内存中只存在一个对象
* 禁止其他程序建立该类对象;(私有化构造方法)
* 避免其他程序建立对象,在本类中自定义一个对象;(在类中创建一个本类对象)
* 对外提供一些访问方式;(提供一个方法,可以得到该对象)
class Single{
private Single(){}
private static Single s= new Single();
public static Single getInstance(){
return s;
}
}
class SingleSet{
public static void main(String[] agrs){
Single s1 = Single.getInstance();
}
}
7.继承
重载:只看参数列表不一样;
重写:子父类方法一模一样;
子类的实例化过程:
- 子父类中的构造方法:在对子类对象进行初始化时,父类的构造方法也会运行,原因是构造方法默认第一行有一条隐式的语句super(),调用父类空参数的构造方法;且所有的子类构造方法都有这条语句;
- 当然子类的构造方法第一行也可以手动指定this语句来访问本类构造方法;子类中至少有一个构造方法会去访问父类构造方法,放在第一行;
final关键字,作为修饰符
继承的优点:提高复用性,引入了多态的概念;
继承的缺点:打破了封装性。
- 可以修饰类,方法,变量;
- 被final修饰的类不可以被继承;防止其中的方法被复写!
- 也可以修饰方法,规定不允许被复写的方法(在有继承类的时候常用);
- 也可以修饰变量,使其变成一个常量,既可以修饰全局变量,也可以修饰局部变量—适用于固定值的数据,所有字母都大写,单词间通过下划线连接;例如:final double CONSTANT_PI = 3.14 通常前面还会加上public和static修饰符,使其可被类名调用,并且全局可用;
- *内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量;
抽象类(没有子类的抽象类没有意义,why?)
多个类中出现相同功能,功能主体不同(方法声明相同,方法体不同);可以向上抽取功能定义,标识为抽象方法(没有方法体);该抽象方法必须存在抽象类中;
- 抽象方法一定抽象在抽象类中,被abstract关键字修饰;但是抽象类中可以包括不抽象的方法;因此,抽象类和一般类没有太大的不同,仅仅是包含了抽象方法;
- 抽象类不可以用new创建对象,因为抽象类中的抽象方法没有具体实现;
- 抽象类中的方法要被使用,必须由子类复写所有的抽象方法,再建立子类对象;
- 抽象类可以强迫子类复写自己的方法!
- 特殊:抽象类中可以不定义抽象方法:基本没有意义,仅仅是不让该类建立对象;
- *常用于模板方法设计模式(常常使用final,abstract关键字):在定义方法时,功能的一部分是确定的(定义为final防止被复写),另一部分不确定,那么就把不确定的部分暴露出去(abstract方法,或者设定默认的方法),让子类去实现;
8 接口
简单理解为:如果抽象类中的所有方法都是抽象时,可以通过接口的形式来表示(一种特殊的抽象类,比抽象类更抽象!);
接口定义时,格式特点:
- 接口中常见定义:常量,抽象方法;
接口中的常见的成员都有固定修饰符(没有默认被补上);
- 常量:public static final
- 方法:public abstract
接口中成员都是public的
- 什么叫继承?父类中有非抽象内容,可以直接拿过来用;
- 什么叫接口?首先肯定不可以实例化;更重要的是子类需要将接口中的抽象方法全部(因此必须是public修饰的暴露出去实现,并且复写时,方法也必须是public修饰的)实现后,才能实例化;
interface Inter{
public static final int NUM = 1;
public abstract void show();
}
class Test implements Inter{
}
实现接口
- Java不可以多类继承,但是支持接口的多实现;
- 可以继承一个类,同时实现多个接口;
- 接口与接口之间的关系:继承关系,且可以多继承:(注意:多继承的接口中不能定义返回值不同的同名抽象方法!)
interface C extends B,A
举例:
abstract class Student{
abstract void study();
void sleep(){
System.out.println("sleep");
}
//不是子类的共性,使用接口来实现abstract void smoke();
}
interface Smoking{
void smoke();
}
class Zhangsan extends Student implements Smoking{
void study(){}
public void smoke(){}
}
/**不属于Student类的teacherA也可以实现Smoking的接口,扩展teacher的功能,
这种Smoking功能,不是所有Teacher类都有的*/
class teacherA extends Teacher implements Smoking{
...
public void smoke(){}
}
9 多态
要有继承,要有重写,要有父类引用指向子类对象;
1. 多态的体现
1. 父类的引用指向了自己的子类对象;
2. 父类的引用也可以接收自己的子类对象(传递参数);
2. 多态的前提
1. 类与类之间需要有关系:继承,实现;
2. 存在方法覆盖,需要使用这个覆盖的方法;
3. 多态的好处
1. 提高了程序的扩展性;
4. 多态的应用
转型
- 向上转型
- 向下转型
class Cat extends Animal{
...
}
Animal a = new Cat();//向上转型
a.eat();
//a.catchMouse();--> 将父类引用转换成子类类型
Cat c = (Cat)a;//向下转型
c.catchMouse();
在多态中,成员方法的特点:
1. 在编译时期:引用所属的类中是否有调用的方法:如果有,编译通过,如果没有(通常会错误使用子类中的方法),编译失败;
2. 在运行时期:引用指向子类创建的对象,通过该对象的引用访问方法,因此,如果父方法中的方法被子方法重写,再使用父类引用调用此方法,将执行的是子类中重新定义的方法;
3. (面试)但是如果父类的方法是static的,则没有此特性,因为只有非静态的方法才可以被重写:在类初始化时,静态方法就已经存在于方法区的静态区(内存)中了,也就是说该方法已经被该类静态绑定,相对的是非静态方法被(例如this关键字)动态绑定;
class Father{
void method1(){//没有返回值的通常叫做工具类方法
}
void method2(){
}
}
class Son extends Father{
void method1(){//重写父类中的method1()方法
}
void method3(){
}
public static void main(String[] agrs){
Father f = new Son();
f.method1();//执行的是被子类重写的方法
f.method2();
//f.method3();编译无法通过!因为在编译时,不创建对象,此时引用型变量f不指向子类对象,当然无法使用子类中才有的方法!
}
}
(面试)在多态中,成员变量的特点:(与方法不同!因为子类变量不是重写父类变量,而是在实例化时,父类和子类的同名变量同时存在于堆内存中?)
* 无论编译还是运行,都参考引用变量所属的类中的成员变量;
原则:静态参考类去确定:静态绑定;非静态参考对象去确定:动态绑定!
10 Object 类(所有类具备的功能)
- boolean equals(Object obj){}
- String toString(){} 通常会重写
- Class getClass(){}
11 内部类
为何要有内部类?要想访问外部类成员,定义方法不就行了么?继承不也可以么?为啥要有内部类?
观点:内部类“实现”了Java多重继承?每个内部类都可以独立继承一个接口(或者具体的类)来实现;
- 作用:可以直接访问外部类的成员(甚至是private修饰的)
- 为什么可以直接访问?内部类中持有了一个外部类的引用;格式:外部类名.this
- 而外部类要访问内部类则必须建立内部类对象
- Outer.Inner in = new Outer().new Inner();
- 所有的类都不能被private修饰:只有内部类(此时处于外部类的成员位置上)可以,将其在外部类中进行封装;
- 也可以被static修饰,此时只能访问外部类的静态成员;
- 其他外部类中,访问外部类静态内部类中的非静态成员:new Outer.Inner().method();
- 其他外部类中,访问外部类静态内部类中的静态方法:Outer.Inner.method();
- 内部类中定义了静态成员,则该内部类也必须是静态的;反之不一定;
局部内部类
不能被private和static修饰,且必须在局部先创建此类对象,才能使用:
* 成员内部类才可以被private,static修饰;
* 局部内部类不可以被private,static修饰,因为private和static都是成员修饰符!;
* 局部内部类可以直接访问外部类的成员,因为它持有一个外部类的引用;但是不可以访问它所在局部中的变量,只能访问被final修饰的局部变量:
class Outer{
int x = 3;
void outermethod(){
//int y = 4;局部内部类需要访问局部变量,需要声明为final
final y = 4;
class Inner{
void inner method(){
System.out.println(x+" "+y);//x前省略Outer.this.x
}
}
}
}
匿名内部类(就是内部类的简写形式)
内部类必须是继承一个类,或者实现一个接口;为何?因为要想实例化这个没有名字的类,只能先去实例化它的父类;被创建的对象,带有内容(对象体)–这个内容是为了实现被继承的类或者接口
class Outer{
int x = 4;
class Inner{
void show(){
System.out.println("x="+x);
}
}
void function(){
new Inner().show();
}
public static void main(String[] agrs){
new Outer().function();
}
}
使用匿名内部类实现:
* new 父类或者接口(){定义子类的内容};
* 匿名内部类就是以这个匿名子类对象,而且这个对象有点胖,可以理解为带内容的对象;
* 匿名内部类中的方法不要超过三个;
abstract class FatherClass{//需要有继承
abstract void show();
}
class Outer{
int x = 4;
void method(){
new FatherClass(){//通过实例化基类对象,获得匿名内部类的内容
void show(){
System.out.println("x="+x);
}
}.show();
}
public static void main(String[] agrs){
new Outer();
}
(面试)