黑马程序员——面向对象

来源:互联网 发布:记事本界面的java代码 编辑:程序博客网 时间:2024/05/02 14:46

——- android培训、java培训、期待与您交流! ———-

什么是面向对象?

例如要买一个电脑,因为我不懂怎么买,不知道怎么看电脑的硬件和不知道怎么砍价。那么我可以找一个懂买电脑的人,
我带他一块去买电脑,我不需要和别人砍价,也不需要知道电脑的配置怎么样才算好,只要交给懂买电脑的人帮我完成这些工作就行。
那么,懂买电脑的人就是一个对象,而我,只是在调用那个人的功能。我不需要知道他是怎么砍价,怎么知道电脑配置好不好。
我只要知道他具备这些功能,而我只需要去调用他的功能就行。

面向对象举例:

1,人们可以通过电脑视频或者聊天,然而,我们不需要知道电脑是如何实现此功能的,我们只需要知道如何去使用电脑,如何去调用电脑的功能就能够达到我们的目的,那么电脑就是一个对象,而我们只是在调用电脑这个对象的功能
2,风扇能旋转扇风,我们不需要知道风扇是如何实现旋转的功能,只需要知道,我们只要打开一个开关,风扇就能转动,转动这个功能属于风扇,而我们只是在调用风扇的开关,那么风扇就相当于一个对象。

面向对象三个特征:封装、继承、多态

封装:

简单的讲,封装就是将复杂的事物封装起来,不让别人看见,然后提供一个对应的访问方式。之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断语句等。 对访问的数据进行操作,提高代码健壮性

代码演示

class Person {    private int age;//将年龄进行封装,设置为私有的    public void setAge(int age) {//对外提供能够设置年龄的方法        //在访问方式中加入逻辑判断语句        if (age > 0 && age < 130) {//如果年龄在0-130之间,那么,视为合法年龄            this.age = age;            speak();        } else//年龄不合法则输出非法年龄            System.out.println("illegal age");    }    public int getAge() {//提供能够获取年龄的方法        return age;    }    public void speak() {        System.out.println("age=" + age);    }}public class Demo {    public static void main(String[] args) {        Person p = new Person();        p.setAge(20);//age=20        // p.setAge(-40);//结果:illegal age    }}

继承:
1.提高了代码的复用性
2.让类与类之前产生了关系。有了这个关系,才有了多态的特性。
(注意:注意:千万不要为了获取其他类的功能,简化代码而继承)

代码演示

class A{        void demo1(){}        void demo2(){}    }    //B为了少写demo1而继承A这种方式是错的,因为B它不仅继承了demo1,同时还继承了demo2,B要继承A,则必须将A中的demo1和demo2都继承    class B extends A{        //void demo1(){}        void demo3(){}    }      //正确的继承方式如下:    class A{        void demo1(){}    }    class B extends A{        //void demo1(){}        void demo2(){}    }    //这是正确的继承方式,B为了省略demo1而继承A中的demo1,C为了省略demo1而继承了A中的demo1。    //B继承了A的全部,C也继承了A的全部,所以正确    class c extends A{        //void demo1(){}        void demo3(){}    }  

多态:
可以理解为事物存在的多种体现形态。
1.多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。

2.多态的前提
必须是类与类之前有关系。
通常还有一个前提:存在覆盖。

3.多态的好处
多态的出现大大提高了程序的扩展性。

4.多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。

代码示例

abstract class DongWu{   //动物类    public abstract void chiFan();  //吃饭方法}class Cat extends DongWu{   //猫类继承动物类    public void chiFan(){        System.out.println("吃鱼");    }    public void zhuaLaoShu(){   //抓老鼠方法        System.out.println("抓老鼠");    }}class Dog extends DongWu{   //狗继承动物类    public void chiFan(){        System.out.println("吃骨头");    }    public void kanJia(){   //看家方法        System.out.println("看家");    }}public class Demo{    public static void main(String[] args){        DongWu d = new Cat();   //向上转型        function(d);        function(new Dog());        /*         结果:            吃鱼            抓老鼠            吃骨头            看家         */    }    public static void function(DongWu d){        d.chiFan();     //此处只能调用父类中的成员        //d.zhuaLaoShu();       //这是错误的,因为父类中没有该方法        //d.kanJia();       //这是错误的,因为父类中没有该方法        if(d instanceof Cat){   //判断传给d的对象是不是Cat            Cat c = (Cat)d;     //向下转型            c.zhuaLaoShu();        }        else if(d instanceof Dog){  //判断传给d的对象是不是Dog            Dog dog = (Dog)d;   //向下转型            dog.kanJia();        }    }}

构造代码块:

作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数执行。

构造代码块和构造函数的区别:
构造代码块:是给所有对象进行统一初始化
构造函数:是给对应的对象进行初始化

构造代码块中定义的是不同对象共性的初始化内容,
例如:
每一个孩子出生时都会哭,那么可以吧哭这个功能放在构造代码块中,
因为哭是每一个孩子都具备的共性内容

代码示例

/*创建一个人对象,使得该对象一出生时,就会哭一次*/class Person{    public String name;    public int age;    //由于每一个孩子一出生都具备"哭"的功能,    //所以可以把"哭"这个功能定义在构造代码块里,这样,    //每当创建一个对象,都会"哭"一次    {        System.out.println("哭");    }    //创建一个对象,接收一个name 和 age    public Person(String name,int age){        this.name = name;        this.age = age;        System.out.println("name="+name+"...age"+age);    }}public class Demo {    public static void main(String[] args) throws Exception {        new Person("zhangsan",0);//创建第一个对象,一创建出来就会哭        new Person("lisi",0);///创建第二个对象,一创建出来就会哭        /*        结果:         哭         name=zhangsan...age0         哭         name=lisi...age0         */    }}
/*  证明构造代码块的执行优先于构造函数 */class Person {    int age;    String name;    // 一旦创建一个对象时,此语句会优先执行,并且优先级高于构造函数    {        System.out.println("构造代码块运行");    }    public Person() {        System.out.println("构造方法Person()运行");    }    public Person(String name) {        System.out.println("构造方法Person(String name)运行");    }}public class Demo {    public static void main(String[] args) {        new Person();//创建一个无参构造函数        new Person("zhangsan");//创建一个参数为字符串的构造函数        /*         结果            构造代码块运行            构造方法Person()运行            构造代码块运行            构造方法Person(String name)运行         */    }}

this:

用于区分局部变量和成员变量同名情况。

this为什么可以解决这个问题?
this到底代表什么呢?

this:就是代表本类的对象,到底代表哪一个呢?
this代表它所在函数所属对象的引用。
简单说:哪个对象在调用this所在的函数,this就代表哪个对象。

代码演示

/*  this代码示例 */class Person {    String name;    int age;    public Person(String name){        this.name = name;    }    public Person(String name, int age) {        this(name);//相当于p(name) --> new Person(name)而且此方法必须放在开头出         this.age = age;// 当p1调用此构造方法时,相当于p1.age = age        System.out.println(name + "..." + age);// 因为是p1在调用,所以打印的结果是p1中的name和age    }    public void setName(String name) {        this.name = name;        System.out.println("name=" + name);    }    public void setAge(int age) {        this.age = age;        System.out.println("age=" + age);    }    // 需求:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人    // this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这是用this来表示这个对象。    // 但凡本类功能内部使用到了本类对象,都使用this表示    public boolean compare(Person p2) {        // 因为是p1在调用此方法,所以this相当于p1,那么相当于p1.age == p2.age        return this.age == p2.age;    }}public class Demo {    public static void main(String[] args) {        Person p1 = new Person("zhangsan", 25);// p1在调用Person的构造方法,那么构造方法中的this相当于p1        p1.setName("ZHANGSAN");// p1在调用Person中的setName方法,那么setName中的this相当于p1        Person p2 = new Person("wangwu", 22);        p2.setName("WANGWU");// 因为p2在调用setName方法,那么setName方法中的this相当于p2        boolean b = p1.compare(p2);        System.out.println(b);        /*         结果:            zhangsan...25            name=ZHANGSAN            wangwu...22            name=WANGWU            false         */    }}

super

子类要访问父类中的同名变量,用super

super和this的区别:
this代表的是本类对象的引用
super代表的是父类对象的引用

代码示例:

class Fu{    int num = 4;}class Zi extends Fu{    int num = 0;    public void show(){        //super关键字代表的是父类的引用,所以super.num使用的是父类中的num        //this代表本类对象,所以this.num输出的是本类中的num        System.out.println("子类的num="+this.num+",父类的num="+super.num);    }}public class Demo {    public static void main(String[] args) throws Exception {        new Zi().show();        //结果:子类的num=0,父类的num=4    }}

static

用法:是一个修饰符,用于修饰成员(成员变量,成员函数)
注意:不能修饰局部变量

当成员被静态修饰后,就多了个调用方式,除了可以被对象调用外,还可以直接被类名调用。类名.静态成员
静态的数据存在于方法区中

static特点:
1.随着类的加载而加载。
也就是说:静态会随着类的消失而消失。说明它的生命周期最长。
2.优先于对象存在
明确一点:静态是先存在,对象是后存在的。
3.被所有对象共享
4.可以直接被类名调用

实例变量(成员变量)和类变量(既被static修饰的变量)区别:
1.存放位置:
类变量随着类的加载而存在于方法区中。
实例变量随着对象的建立而存在于堆内存中。
2.生命周期:
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失

静态使用注意事项:
1.静态方法只能访问静态成员。
非静态方法既可以访问静态也可以访问非静态。
2.静态方法中不可以定义this,super关键字。
因为静态方法优先于对象存在。所以静态方法中不可以出现this。

静态有利有弊

利处:1.对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份
2.可以直接被类名调用。
弊端:1.生命周期过长。
2.访问出现局限性。(静态虽好,但只能访问静态)

静态变量什么时候使用:当对象中出现共享数据时,该数据被静态修饰
静态方法什么时候使用:当方法没有访问到非静态数据(对象的特有数据)
那么该方法可以定义为静态的

注意:局部变量不可以设置为静态的(例如:在方法中不可以定义static变量,因为在方法中的数据都是局部的)

静态代码块

特点:随着类的加载而执行,优先于主函数执行,而且只执行一次。因为类加载进内存只加载一次
作用:给类进行初始化

代码示例

class Demo1 {    static{        System.out.println("a");    }}public class Demo {    static{        System.out.println("b");    }    public static void main(String[] args) {        new Demo1();        new Demo1();        System.out.println("over");    }    static{        System.out.println("c");    }}//执行结果: b  c  a  over

面试中的重点:懒汉式与饿汉式

(面试专用题,必考题,不懂就背下来)
注意:懒汉式和饿汉式有什么不同?懒汉式的特点在于延迟加载,饿汉式不延迟加载。
懒汉式的延迟加载有没有问题?有,如果多线程访问时会出现安全问题。
怎么解决?可以加同步来解决,用同步代码块和同步方法都行,但是稍微有些低效。
用双重判断方式可以解决这个效率问题。
加同步时候,使用的锁是哪一个?该类所属的字节码文件对象。

饿汉式:Single类一进内存,就已经创建好了Single对象。定义单例建议使用饿汉式

class Single{    private static final Single s = new Single();//一进内存就创建好了对象    private Single(){}    public static Single getSingle(){        return s    }}

懒汉式: Single类进内存,对象还没有存在,只有调用了getSingle方法时,才建立Single对象

class Single{    private static Single s = null;//进内存时,还没有对象    private Single(){}    public static Single getSingle(){        if (s == null) {            synchronized (Single.class) {                if (s == null) {                    s = new Single();//调用了getSingle方法时,才建立Single对象                }            }        }        return s;    }}

final:

1.可以修饰类,函数,变量
2.被final修饰的类不可以被继承
3.被final修饰的方法不可以被覆盖,但是可以重载
4.被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员变量,也可以修饰局部变量
注意:static只能修饰成员变量)
5.书写规范:所有字母都大写,如果由多个单词组成,那么就通过 _ 连接

覆盖:

当子类出现和父类一模一样的函数时,当子类对象调用
该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。

这种情况是函数的另一个特性:重写(覆盖)

当子类继承父类,沿袭了父类的功能到子类中,
但是子类虽具备该功能,但是功能内容却和父类不一致,
这时,没有必要定义新功能,而是用覆盖特性,保留父类的功能定义,
并重写功能内容

注意

1,子类覆盖父类,必须保证子类权限大与等于父类权限,才可以覆盖,否则编译失败。
2,静态不能覆盖非静态,静态只能覆盖静态,

记住
重载:只看同名的参数列表。
覆盖:子父类方法要一模一样(子类的权限必须是大于或等于父类的权限)。

子类构造函数特点:

子类所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有一句隐式super();

当父类中没有有空参数的构造函数时,
子类必须手动通过super语句形式来指定要访问父类中的构造函数。

当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数

代码示例:

class Fu {    public Fu(String name) {        System.out.println("fu..." + name);    }}class Zi extends Fu {    public Zi() {        // super();//此语句不能执行,因为父类中没有空构造函数        super("haha"); // 因为父类中没有默认空构造函数,子类必须手动通过super语句制定要访问的父类构造函数        System.out.println("zi..run");    }    public Zi(String name) {        this();         // super(name);//错误,因为super()必须放在构造函数的第一行,一个构造函数中,super和this只能存在一个        System.out.println("zi..." + name);    }}public class Demo {    public static void main(String[] args) {        new Zi();//fu...haha    zi..run        new Zi("haha");//fu...haha      zi..run     zi...haha    }   }

抽象类:

当多个类中出现相同功能,但是功能主体不同,
这时可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体

抽象:看不懂的东西就叫抽象
抽象类的特点:
1.抽象方法一定在抽象类中
2.抽象方法和抽象类必须由abstract关键字修饰
3.抽象类不可以用new创建对象,因为抽象方法没有方法体,调用该抽象方法没意义。
4.抽象类中的方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么子类必须也为抽象类

抽象类和一般的类没有太大的不同。
该如何描述事物就如何描述事物,只不过,该事物出现了一些看不懂的东西。
这些不确定的部分也是该事物的功能,需要明确出现。但是无法定义主体,
通过抽象方法来表示。

抽象类和普通类比较:抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法。抽象类不可以创建对象。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让类建立对象

什么时候用抽象方法:子类必须从父类拿过来且需要自己改写的,就定义为抽象方法(注意:是必须拿过来)。子类
需要原封不动的拿过来用的,就定义成非抽象方法。

代码示例:

/* * 需求: *对员工建模,员工包含3个属性:姓名、工号、工资。经理也是员工,除了含有员工的属性外, *另外还有一个奖金属性。请用继承的思想设计出员工类和经理类,要求类中提供必要的方法 *进行属性访问 */abstract class YuanGongDemo {    private String name;    private int gonghao;    private int gongzi;    public YuanGongDemo(String name, int gonghao, int gongzi) {        this.name = name;        this.gonghao = gonghao;        this.gongzi = gongzi;    }    /*    因为员工必须要工作,但是工作的形式又不同,所以定义为抽象的    例如:员工的工作是敲代码,经理也是员工,但是经理的工作是负责看管其他的员工敲代,          经理的工作和员工都必须工作,但是他们的工作不同,所以定义为抽象的。    简单的说:当多个类中出现相同功能,但是功能主体不同,那就可以把该功能定义为抽象的    */    public abstract void gongzuo();//由于每个员工都需要工作,                                    //但是工作的方式不同,所以定义为抽象的}class YuanGong extends YuanGongDemo{    public YuanGong(String name, int gonghao, int gongzi) {        super(name, gonghao, gongzi);    }    public void gongzuo(){        System.out.println("敲代码");    }}class JingLi extends YuanGongDemo {    private int jiangjin;    public JingLi(String name, int gonghao, int gongzi, int jiangjin) {        super(name, gonghao, gongzi);        this.jiangjin = jiangjin;    }    public void gongzuo(){        System.out.println("看管员工敲代码");    }}

接口:

interface 用于定义接口

接口定义时,格式特点:
1.接口中常见定义:常量、抽象方法。
2.接口中的成员都有固定的修饰符
常量:public static final
方法:public abstract
记住:接口中的成员都是public的。

接口不可以创建对象,因为有抽象方法。
需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化(创建对象)。
否则子类是一个抽象类

接口可以多继承

接口:用于定义扩展功能
类: 用于定义基本功能

如:
1.在学生体系中,学生有学习和睡觉,学习和睡觉是基本功能。
而有些学生抽烟,抽烟是扩展功能,因为抽烟并不是每个学生都必须具备的功能。
2.在运动员体系中,运动员有打球和睡觉,打球和睡觉是基本功能。
而有些运动员学习java,学习java 是扩展功能,因为并不是每个运动员都必须要学习java

代码示例:

 /*    接口的多继承 */interface A{    public abstract methodA();}interface B{    public abstract methodB();}interface C extends A,B{//C继承A的同时继承B    public abstract methodC();}
 /*    接口的使用方式 *///定义一个接口interface Somking{    //抽烟的方法。因为抽象不是学生该具备的功能,它属于扩展功能,所以定义在接口中    public abstract void somke();}//定义一个学生类,每一学生都需要睡觉和学习,将其方法封装在学生类中abstract class Student{    abstract void study();//学习的方法,因为每个学生都要学习,但是学习的的方式不同,所以定义为抽象    void sleep(){        System.out.println("睡觉");//睡觉方法。因为每个学生睡觉的方式都一样,所以不是抽象的    }}class ZhangSan extends Student{    void study() {        System.out.println("zhangsan:学习java");    }}class LiSi extends Student implements Somking{//因为lisi抽烟,所以事先Somking接口    void study() {        System.out.println("lisi:学习c++");    }    public void somke() {        System.out.println("lisi:抽烟");    }}public class Demo {    public static void main(String[] args) {        ZhangSan zs = new ZhangSan();        zs.study();//zhangsan:学习java        zs.sleep();//睡觉        LiSi ls = new LiSi();        ls.study();//lisi:学习c++        ls.sleep();//睡觉        ls.somke();//lisi:抽烟    }}

一些需要注意的细节:

    直接输出一个对象时,调用的是对象.toString()方法输出一个字符串。此方法是Object中的方法,    默认输出的结果是:getClass().getName() + '@' + Integer.toHexString(hashCode())                   既:对象名@十六进制的哈希码。 如:Demo@6af62373                             Demo是对象名,6af62373是十六进制的哈希码    想要输出指定的值,对象可以覆盖Object类中的toString,返回一个自己希望的字符串,    那么再直接输出一个对象时,输出结果就是自己定义的那个字符串   

面向对象的简单总结:

1,以后开发:其实就是找对象使用。没有对象,就创建一个对象。2,类:就是对现实生活中事物的描述   对象:就是这类事物,实实在在存在的物体3,引用数据类型包含三种:数组、类、接口   只要new一次对象,堆内存中就产生一个新对象   堆内存中的变量:一被定义都有一个默认的值4,成员变量和局部变量:    作用范围:        成员变量:作用于整个类中。定义在类中的变量。        局部变量:作用于函数中,或者语句中。    在内存中的位置:        成员变量:在堆内存中,因为对象的存在,才在内存中存在。定义在类中的变                 量都是成员变量。5,匿名对象使用方式一:        当对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。        如果一个对象进行多个成员调用,必须给这个对象起个名字   匿名对象使用方式二:        可以将匿名对象作为实际参数进行传递6,构造代码块:        作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数执行。7,this:this代表它所在函数所属对象的引用。简单说:哪个对象在调用this所在的   函数,this就代表哪个对象。8,当成员被静态修饰后,就多了个调用方式,除了可以被对象调用外,还可以直接被   类名调用。类名.静态成员。静态的数据存在于方法区中   static特点:        1.随着类的加载而加载。          也就是说:静态会随着类的消失而消失。说明它的生命周期最长。        2.优先于对象存在          明确一点:静态是先存在,对象是后存在的。        3.被所有对象共享        4.可以直接被类名调用9,final:   1.可以修饰类,函数,变量            2.被final修饰的类不可以被继承            3.被final修饰的方法不可以被覆盖,但是可以重载            4.被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员            变量,也可以修饰局部变量            (注意:static只能修饰成员变量)            5.书写规范:所有字母都大写,如果由多个单词组成,那么就通过 _             连接10,在多态中成员函数的特点:   在编译时期:参阅引用变量所属的类中是否有调用的方法(左边)。如果有,编译   通过,如果没有编译失败。   在运行时期:参阅对象所属的类中是否有调用的方法(右边),如果有,则执行右   边,没有,则执行左边   简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。   在多态中,成员变量的特点:   无论在运行还是编译时,都参考左边(引用型变量所属的类)。   在多态中,静态成员函数的特点:   无论运行还是编译时,都参考左边   在多态中,静态变量的特点:   无论运行还是编译时,都参考左边
0 0
原创粉丝点击