java基础篇——面向对象

作者:xcbeyond
疯狂源自梦想,技术成就辉煌!微信公众号:《程序猿技术大咖》号主,专注后端开发多年,拥有丰富的研发经验,乐于技术输出、分享,现阶段从事微服务架构项目的研发工作,涉及架构设计、技术选型、业务研发等工作。对于Java、微服务、数据库、Docker有深入了解,并有大量的调优经验。

引言:

        面向对象的思想正是Java学习的核心部分,要是没有搞懂面向对象,那么就称不上学过Java,因此,搞懂面向对象是至关重要的。对于初学者而言,首次接触面向对象时,总感觉怪怪的,不知道究竟在干什么(我刚开始接触也是这种感觉,渐渐的就找到感觉了),这其实都是正常现象的,不然怎么有人说他是很抽象的啊。面向对象,简单的理解就是把一切事物按照它自己本有存在的特征、属性通过自然组织语言组织起来,然后再用编程语言来实现。例如:人(类)本该就分有性别(男、女)、年龄、姓名等属性特征,然对于每一个而言,他们往往具体不同的这些属性特征值,于是就从“人”(类)这个类别中具体出来了每个人,即:类的实例化得到了对象。就可以对象他们进行相应的操作了。

面向对象(OO):

         按照东西的特征和自然组织形式, 进行软件开发过程的组织, 是一个开发过程的方法论。

 

学习面向对象:

      - 学习如何用OO语法描述事物的特征和自然组织形式.

      - 学习面向对象思维的前提是朴素的哲学逻辑.

            如: 多态, 抽象概念的具体实现是多态的,

             如:美女是多态的!

 

一、类(Class):

       它是一个复杂的数据结构,是把一类相似或相关联数据的相关操作封装在一起的一个集合。

  1、类是用来描述一个领域模型中的具体概念(名词)的。

  2、领域模型: 一个应用软件业务范畴, 也叫业务模型.

  3、属性: 描述具体概念(事物)的特征.

    如:<图书馆管理系统>中的概念: 书 Book

        书的特征: 书号, 书名, 编号, 作者, 价格...

 

Java中类的写法(语法)如下:

 修饰词 class 类名{

   修饰词 类型 属性;

  修饰符 返回值类型  方法名 {

      //  方法体(实现类的相关操作)

      }

  }

如:

     public class Book{
          int id;
          String name;
          String[] authors;
          String isbn;
          double price;
      }


 

二、对象:

  1、      对象是类的具体实例!

           比 如,书Book这个类,它只是代表了一类,你却不知道它到底是什么书,但你只知道它拥有哪些特点属性,即书名、书号、作者、书价等(类属性)信息,为了对书Book这个类具体化,就引出了“对象”的概念,因此,对象就是类的具体实例。如:《山楂树之恋》是一本书,《和空姐同居的日子》是一本书,他们属于Book类的两个不同的对象。

          对象的创建是通过“new”运算符进行实例化的,即:

                      

              Book book = new Book();//book:引用 new Book():对象

        看到这里,你或许还是一头雾水,请不必担心,先暂时记住就行了,随后你就会彻底明白了。new Book()它是真正的在内存中(准确的说是在“堆”内存中)创建了一个Book类的对象,此时在“栈”中创建了一个Book类型(类也属于一种数据类型,一种引用类型中的“类类型”)的引用变量book,最终将变量book的地址指向了new Book()创建的对象。<这里地址指向关系,是隐含存在的一种指针,可以这么理解,仅供理解而已。但java却是不存在指针的哦!>   。关于“堆”、“栈”可以参考《java核心内容——分配管理》了解。

     引用:是指向具体对象的句柄,相当于自然语言的代词。   

       1> 代词本身不是对象,代词引用了一个具体对象。

        2> 在特殊情况下引用(代词)可能指空。

        3> 经常简单的叙述事物时候,不严格区别引用与对象。

       如:  

       Book book = new Book();//book:引用   new Book():对象  
     
        book.name = "月子";
     
       book.authors = new String[]{"白云","黑土"};
     
       book = null;//book引用null  
     
     

      4>引用是null时候,访问属性或方法时候会出现: 空指针异常

     如:

    book = null;
     
    System.out.println(book.name);//异常   
     

2、构造器(构造方法):

       在上面语句Book   book  =   new  Book()中,或许对用new  Book()创建对象时,并不知道其真正的含义,其中Book()就是一个Book类的一个构造器(特殊的方法),用它来对对象的属性进行初始化。

构造器的使用:

 1>、声明在类内部, 方法名与类名一致的方法叫构造方法, 构造方法不能声明返回值类型。

 2>、构造方法可以包含参数, 参数一般是创建对象实例,必须依赖的条件(前提条件)。

如:

    public Book(int id,String name,String isbn) {
     
    this.id = id;
     
    this.name = name;
     
    this.isbn = isbn;
     
    }
     

  3>、构造方法经常会重载

        方法重载:方法的重新书写,即重载

               - 方法名一样

              - 方法参数不一样

 

默认构造器:

            对于每一个类都会存在一个构造器的,只是有些是使用的默认构造器而已,没有显式的显示出来。

  1>、如果类没有声明任何构造器,Javac自动提供一个默认构造器, 无参数默认构造器。

  2>、如果提供构造器声明, Javac将不再提供默认构 造器。

如:(这个程序只是为了说明默认构造器而已,没有其他实际价值)

    /**
     * 默认构造器的演示
     * @author xcbeyond
     *
     */
    public class ConstructorDemo {
        public static void main(String[] args) {
            Foo foo = new Foo();
            //Goo goo = new Goo();//错误,没有参数的构造器
            //System.out.println(foo.a+","+goo.a);
            //A 、编译错误         B、 1,0   C、 1,1  D、
        }
    }
    class Foo {//没有显式的显示构造器,即使用默认构造器(无参构造器)
        int a = 1;
    }
    class Goo{//没有无参构造器了
        int a ;
        public Goo(int a) {//默认无参构造器被重载成为了一个有参构造器
            this.a = a;
        }
    }

 

new运算: 创建对象实例,如: Book book = new Book();

  1、根据类的属性在堆中分配对象空间。






  2、根据参数类型调用构造器。

  3、new运算返回对象的引用地址。

 

对象的创建过程:

  1、根据类的属性在堆中分配对象空间,并且自动初始化。

  2、根据参数类型调用构造器。

 

this:

  最大的作用就是让类中一个方法访问该类的另一个方法或属性。this可以代表任何对象。

   - this 是对当前对象的引用, 是当前对象本身。

   - 可以使用this明确的访问当前对象的属性或者方法。

   - this() 是调用本类的其他构造器, 可以使用构造器的重用, 简化代码的实现.

   - this() 必须写在构造器的第一行!

    如:public Book(int id ,String name) {
                //System.out.print(id);//错 ,this()必须写在第一行
            this(id,name,null);
        }
        public Book(int id,String name,String isbn) {
            this.id = id;
            this.name = name;
            this.isbn = isbn;
        }


 

java方法参数的传递规则:

             参数传递方式只有一种:值传递。值传递就是将实际参数值得副本(复制品)传入方法内,而参数本身不会受到任何影响。

 1、基本类型就是其中值的复制, 引用类型是引用值(地址)的复制。

 2、 变量的值:

    - 基本类型的值是其本身

    - 引用变量的值是一个地址值,是被引用对象的首地址

 3、为了避免引用参数传递的副作用, 建议一切结果使用返回值带回。

如:

    /**
     * Java方法参数传递规则:基于值的传递,是变量值的复制
     *     为了避免引用方法参数传递的副作用,建议一切结果使用返回值返回
     * @author xcbeyond
     */
    public class ParamaterDemo {
        public static void main(String[] args) {
            Koo koo = new Koo();
            int a = 1;
            update(a);
            update(koo);
            System.out.println("a="+a+",koo.a="+koo.a);//a=1,koo.a=2
            
            int b = update2(a);
            int c = update2(koo);
            System.out.println("b="+b+",c="+c);//2,3
            
        }
        
        public static void update(int a ) {
            a++;
        }
        public static void update(Koo koo) {
            koo.a++;
        }
        
        public static int  update2(int a ) {
            a++;
            return a;
        }
        public static int update2(Koo koo) {
            koo.a++;
            return koo.a;
        }
    }
     
    class Koo {
        int a = 1;
    }

 

3、继承:

    考虑到程序的实时扩展(添加新的属性、方法等操作),更好的进行维护,于是引入“继承”这个概念。它是在原有类的基础上添加或修改(重写)新的属性和方法。子类继承了父类的所有方法和属性。

    用来表达类型概念上具体化,Java继承用extends 关键字。子类是父类的具体化,父类是子类的泛化(概念抽象化)

 1>、子类继承父类的属性和方法

 2>、构造器不能继承!

 3>、实例化子类,会递归分配所有父类的空间

 4>、子类默认调用父类的无参数构造器

继承特点:

  1>、子类继承父类的属性和方法

  2>、构造器不能继承

  3>、创建子类实例,会递归分配所有父类的空间

  4>、子类默认调用父类的无参数构造器

  5>、子类可以覆盖(重写)父类的方法,修改父类行为

  6>、JAVA只能单一继承

继承中的语法现象:

  1>, 父类可以引用子类的实例,父类的实现是多态的。

  2>, 子类可以覆盖父类的方法,修改父类的行为。

     - 方法覆盖:子类覆盖了父类“相同方法签名”的方法。

     - 方法的覆盖是由方法动态绑定实现的,就是java虚拟机运行时候确定执行那个方法,java最终执行子类的方法。

关于继承中的构造器:

  1> 、子类递归调用父类构造器。

  2> 、默认调用父类无参数构造器!

  3> 、如果父类没有无参数构造器,就必须在子类中明确指定调用父类的有参数构造器!

  4> 、使用super()调用父类构造器,必须写在子类构造器第一行。

  5> 、编程建议:所有的类都提供无参数构造器!减少继承时候的麻烦。


如:

 

    /**
     * 子类构造器一定调用父类的构造器!
     * @author xcbeyond
     *
     */
     
    public class ConstructorDemo {
        public static void main(String[] args) {
            Goo goo = new Goo();
            System.out.println(goo.a);//5
        }
    }
     
    class Foo extends Object {//任何类都继承Object类(超类)
        int a = 1;
     
        public Foo(int a) {
            super();//Object类的构造器
            this.a = a;
        }
        
        public void Foo() {//构造方法没有返回类型值,这里并不是构造方法,而是一个一般的方法而已!
            
        }
    }
     
    //class Goo extends Foo{}  //错 , 父类没有无参数构造器
    class Goo extends Foo{
        public Goo() {
            //System.out.println();//super()必须放在构造器的第一行
            super(5);//明确调用父类由参数构造器
        }
    }

 

对象的实例化过程

  1>、 检查类是否加载,如果没有加载就加载这个类

    - 如果有父类,要加载所有父类

    - 懒惰式加载,如果第一次用到就加载,只加载一次

    - 通过CLASSPATH指定的路径寻找类文件

    - 加载以后是一个对象,类型是Class

  2>、在内存堆中分配对象空间

      递归分配所以父类属性空间,然后分别子类的空间,属性默认自动初始化,自动初始化为“0”值:null,0,false,\u0000

  3>、执行属性的赋值

  4>、递归调用父类构造器。(默认调用父类无参数构造器!)

  5>、调用本类构造器

 

访问控制:

   访问控制修饰词:public  protected  default  private

    - 声明属性和方法尽可能私有,这样才能做到尽可能封装

    - 提供适当的属性访问方法,适当的开放属性的访问

    - 不建议使用非公有类。就是说所有类都应该是公有的,并且一个源文件一个类

 

属性是静态绑定到变量类型。

    方法是动态绑定,由最终对象的方法决定。

如:

    /**
     * 属性是静态绑定到变量类型
     * 方法是动态绑定,有最终对象的方法决定
     * @author xcbeyond
     *
     */
    public class FiledAccessDemo {
        public static void main(String[] args) {
            Goo goo = new Goo();
            Foo foo = goo;
            
            System.out.println(goo.a+","+goo.getA());//2,2
            System.out.println(foo.a+","+foo.getA());//1,2
        }
     
    }
    class Foo {
        int a = 1;
        public int getA() {
            return a;
        }
    }
    class Goo extends Foo {
        int a = 2;
        public int getA() {
            return a;     //retrun super.a;
        }
    }