面向对象编程
1. 面向对象
- 面向对象思想:
不同于面向过程思想多用于处理一些步骤清晰简单的问题
面向过程适合处理复杂,需要多人协作的问题.
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思维去处理。
1.1 类和对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。
动物、植物、手机、电脑… Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
- 对象是抽象概念的具体实例
张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。
Hero h = new Hero()
创建了一个Hero对象,并且引用变量h来指向它。
多个引用可以指向同一个对象,但是一个引用同一时间只能指向一个对象。
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
一个类里面只有属性和方法。
2. 面向对象编程
面向对象编程(Object-Oriented Programming, OOP):
- 本质是以类的方式组织代码,以对象的组织(封装)数据。
- 核心:抽象
- 三大特性:封装、继承、多态
2.1 封装
- 高内聚,低耦合:高内聚就是将类的内部数据操作细节自己完成,不允许外部干涉。低耦合就是尽量暴露少量的方法给外部使用。
- 数据的隐藏:禁止访问一个对象中数据的实际表示,而是通过操作接口来访问。属性的私有: private;get/set;
2.2 继承
- 继承
- java中只有单继承,没有多继承
- 继承是类和类之间的关系,除此之外还有依赖、组合、聚合等
- 继承关系的两个类分别为:子类(派生类)、父类(基类)
- 子类和符类在意义上有“is a”的关系
子类名+extend+父类名
2.2.1 继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
- 如果父类是抽象类,那么子类一定要实现父类的抽象方法,如果父类是普通类,那么子类可以不重现父类的方法。
java不支持多继承,但是支持多重继承:
2.2.2 继承关键字
extend关键字:java中类是单一继承,所以extend只能继承一个类,可以
public class Animal {
private String name;
private int id;
public Animal(String myName, String myid) {
//初始化属性值
}
public void eat() { //吃东西方法的具体实现 }
public void sleep() { //睡觉方法的具体实现 }
}
public class Penguin extends Animal{
}
implements关键字:使java具有多继承的特性,使用范围为类继承接口的情况,实现了多个接口的继承()接口跟接口之间采用逗号分隔。
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
super使用的注意点:
1.super 调用父类的构造方法,必须在构造方法的第一个
2.super 必须只能出现在子类的方法或者构造方法中!
3.super 和 this 不能同时调用构造方法!
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。
2.3 多态
多态可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
- 多态
- 多态是方法的多态,属性没有多态性
- 父类和子类,有联系才能转换,否则会发生异常,类型转换异常:ClassCastException
- 存在条件:继承关系
父类的抽象方法,即abstract修饰的方法需要重写,父类为普通方法可以重写也可以不重写。不能重写的情况:
1.static方法,它属于类,不属于实例
2.final常量,被final修饰的无法修改,属于常量池
3.private私有方法,不能被重写
2.3.1 操作符的多态
同一个操作符在不同情境下,具备不同的作用
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接
2.3.2 类的多态
1.父类的引用可以指向子类,但不能调用子类独有的方法。
2.方法的调用只和左边定义的数据类型有关,和右边关系不大。
3.动态编译:类型:可扩展性更强
4.即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
//子类能调用的方法都是自己的或继承父类的
Student s1 = new Student();//子类
//父类可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();//父类
Object s3 = new Student();
/*一个对象的实际类型是确定的
可指向的引用类型不确定,父类的引用指向子类
*/
//但是子类重写父类方法后,指向子类的方法
s2.run();//父类有,子类没有,子类继承父类方法
s1.run();//子类重写后,执行子类方法
s1.eat();//子类独有方法
((Student) s2).eat();
//父类不能调用子类独有方法,会被强制转换为子类
判断对象类型:
instanceof:判断一个对象是什么类型
eg:System.out.println(x instanceof y); :true or false (能不能编译通过,看x所指向的实际类型是不是y的子类型)
转换:
- 父类引用指向子类的对象,不可以子类引用指向父类。
- 把子类转换为父类,向上转型;
- 把父类转换为子类,向下转型,强制转换(可能会丢失方法)
- 方便方法的调用,减少重复的代码,简介
//类型之间的转化 : 父---子
//高 低
Person s1 = new Student();
//高转低可以直接转;低转高,需要强制转
//
Student s2 = (Student) s1;
s2.go();
//或((Student) s1).go();
2.4 static关键字详解
静态方法在类的时候就已经加载了
static加上方式叫静态方式,加上属性叫静态属性
2.4.1 静态属性
public class Student {
private static int age;//静态的变量,可以被类中共享,多线程比较常用
private double score;//非静态变量
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.age);//通过方法可以正常调用
System.out.println(s1.score);
System.out.println(Student.age);//静态变量,可以直接用类名进行调用
//System.out.println(Student.score);//非静态变量不可以。
}
}
2.4.2 静态方法
public class Student {
public void run(){
Student.go();
}
public static void go(){
}
public static void main(String[] args) {
Student.go();//静态方法不需要 new 可以直接调用
//由于静态方法在类生成的时候就已经存在,所以可以调用静态的
//Student.run(); 但不能调用非静态的,非静态方法需要 new 出来
new Student().run();
}//注解和反射
}
2.4.3 代码块
{
//代码块(匿名代码块)
}//创建对象的时候就已经创建了,在构造器之前
static {
//静态代码块
}//在类一加载就已经执行,而且只加载一次
2.4.4 加载顺序
static只执行一次
public class Demo03 {
//第二加载;适用于赋初值
{
System.out.println("匿名代码块");
}
//第一加载;只在第一次执行
static{
System.out.println("静态代码块");
}
//第三加载
public Demo03() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Demo03 s1 = new Demo03();
System.out.println("==========");
Demo03 s2 = new Demo03();
//第二次执行static不在执行
}
}
2.4.5 静态导入包
静态导入包后可以直接调用其方法:
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Demo03 {
public static void main(String[] args) {
System.out.println((int)(Math.random()*50));
//random()随机值,整数,范围(0-50)
//使用静态导入包后可以直接System.out.println(random());
System.out.println(PI);
}
}
2.5 抽象类
在类中声明一个方法,这个方法没有实现体,是一个“空”方法,这样的方法就叫抽象方法,使用修饰符“abstract",当一个类有抽象方法的时候,该类必须被声明为抽象类。
- abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用 new 关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法;否者该子类也要声明为抽象类,然后由子类实现抽象方法。
//abstract 抽象类 类 extends,单继承; (接口可以多继承)
public abstract class Action {
//约束~有人帮我们实现
//abstract,抽象方法,只有方法名字,没有方法的实现!
public abstract void doSomething();
}
//抽象类的所有方法,必继承了它的子类,都必须要实现它的方法
public class A extends Action {
//除非子类也是抽象方法,那就由子类实现
@Override
public void doSomething() {
}
}
2.6 接口
2.6.1 定义
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:属于一种约定
声明类的关键字是class,声明接口的关键字是interface
2.6.2 作用
- 约束,规范
- 定义一些方法,让不同的人实现。多个人完成共同的工作。
- 接口中所有默认的方法public abstract
- 所有常量默认public static final
- 接口不能被实例化,接口中没有构造方法。
- 可以实现多个接口
- 必须要重写接口中的方法。
- 声明接口interface,实现接口implements,可以实现多个方法
//interface 接口定义的关键字;接口都需要实现类
public interface Uservrvice {
// public void ss(){ } 报错;接口内不能写方法
//接口中的所有定义其实都是抽象的,默认 public abstract
public abstract void run();
void add(String name);
void delete(String name);
//接口还可以定义变量,所有定义的属性都是静态的常量
public static final int AGE = 99;
int ABC = 99;
}
2.6.3 抽象类和接口的区别
抽象类 | 接口 |
---|---|
子类只能继承一个抽象类,不能继承多个 | 子类可以实现多个接口 |
抽象类可以定义public,protected,package,private、静态和非静态属、final和非final属性 | 接口中声明的属性,只能是public、静态、final的即便没有显式的声明 |
2.7 内部类
内部类是指在一个外部类的内部再定义一个类,内部类一般来说包括这四种:成员内部类、局部内部类、静态内部类和匿名内部类。
- 内部类可以是静态static的,也可以用public,defult,protected和private修饰
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。
2.7.1 成员内部类
最普通的内部类,定义于另一个类的内部,特点如下:
- 外部类想要访问成员内部类,需要先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
public class Winner {
String name;
int age;
String country;
class winGame{
int score;
//非静态内部类,只有在外部对象存在才有意义
//当比赛的人存在,他的胜利才有意义
public void win(){
if (score>11) {
System.out.println(name + "赢得了比赛");
}
else
System.out.println(name + "尚未赢得比赛");
}
}
public static void main(String[] args) {
Winner Deng = new Winner();
Deng.name ="邓丽萍";
//实例化一个对象,并且在此对象的基础上建立实例化内部类
winGame sco = Deng.new winGame();
sco.score = 12;
sco.win();
}
}
- 成员内部类就是作为外部类的成员,可以无条件访问外部类所有成员和方法,包括private成员和静态成员
- 成员内部类不能含有static的变量和方法,因为成员内部类必须先创建了外部类,才能创建它自己的类。
class Outter {
private double radius = 0;
public Outer(double radius){ //有参的构造方法
this.radius =radius;
getDrawInstance().drawShape();//得到成员内部类的对象
}
public Draw getDrawInstance() {
return new Draw();
}
class Draw{
public void drawShape(){
System.out.println(radius);
}
}
public static void main(String[] args) {
Outter out = new Outter(1.2);
}
}
小结:
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。
- 用private修饰,则只能在外部类的内部访问
- 如果用public修饰,则任何地方都能访问;
- 如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;
- 如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。个人理解:由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
2.7.2 局部内部类
局部内部类——就是定义在一个方法或者一个作用域里面的类
特点:主要是作用域发生了变化,只能在自身所在方法和属性中被使用
//在局部位置,可以创建内部类对象,通过对象调用和内部类方法
class Outter {
private int age = 10;
public void method(){
final int age1 = 20; //final修饰后,该值使用完毕后就消失,即age1存储的是常量20,而不是变量名
class Inner{
public void show(){
System.out.println(age);
System.out.println(age1);
}
}
Inner i = new Inner();
i.show();
}
public static void main(String[] args) {
Outter outter.method();
}
}
2.7.3 静态内部类
static是不能用来修饰类的,但是成员内部类可以看做外部类中的一个成员,所以可以用static修饰,这种用static修饰的内部类我们称作静态内部类,也称作嵌套内部类.
但是因为静态内部类不需要外部类的实例,因此不能使用外部类的非static成员变量和成员方法
class Outter {
int age = 10;
static int age2 = 20;
public Outter() {
}
static class Inner {
public void method() {
// System.out.println(age);//错误
System.out.println(age2);//正确
}
}
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
inner.method();
}
}
2.8.4 匿名内部类
匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
它继承了一个父类或者接口后,直接实例化一个抽象类,并且实现该抽象方法
这样的类,叫做匿名类
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
在接口上使用匿名内部类:
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
区别:
不适应匿名内部类
public class Test {
public static void main(String[] args) {
//如果我们需要使用接口中的方法,我们就需要走3步,1、实现接口 2、创建实现接口类的实 例对象 3、通过对象调用方法
//第二步
Test02 test = new Test02();
//第三步
test.method();
}
}//接口Test1
interface Test01{
public void method();
}
//第一步、实现Test01接口
class Test02 implements Test01{
@Override
public void method() {
System.out.println("实现了Test接口的方法");
}
}
使用匿名内部类
public class Test {
public static void main(String[] args) {
//如果我们需要使用接口中的方法,我们只需要走一步,就是使用匿名内部类,直接将其 类的对象创建出来。
new Test1(){
public void method()
{
System.out.println("实现了Test接口的方法");
}
}.method();
}
}
interface Test1{
public void method();
}
解析:其实只要明白一点,new Test1(){实现接口中方法的代码}; Test1(){…}这个的作用就是将接口给实
现了,只不过这里实现该接口的是一个匿名类,也就是说这个类没名字,
只能使用这一次,我们知道了这是一个类, 将其new出来,就能获得一个实现了Test1接口的类的实例
对象,通过该实例对象,就能调用该类中的方法了,因为其匿名类是在一个类中实现的。