• java基础(三):面向对象(上)


     

    五、面向对象

     

    5.1、类和对象

      类的三个最常见成员构造器、属性、方法

      属性修饰符:public(protected、private)、static、final

      方法修饰符:public(protected、private)、static、final(abstract)

      

      1、静态成员和非静态成员

      static 修饰的属性、方法称为静态属性、静态方法,属于类共有类 和 实例 都可以调用,否则只能实例调用)。

      静态成员(属于类共有,类调用时没有创建实例)不能直接访问非静态成员(非静态成员必须使用实例来调用,如main方法要new对象);

     

      2、对象的产生、引用和指针  

      Person p = new Person();

      创建对象:通过new关键字调用Person类的构造器,返回一个Person实例(创建一个对象)并赋值给p变量;

      JAVA除了8种基本数据类型,还有引用类型:类、数组、接口、null类型; 

      上面代码产生两个实体:p变量、Person对象。因为类是引用数据类型,所以Person对象赋值给p变量时只是把对象的引用赋值给了p(存放在栈内存中),真正的Person对象则是存放在堆内存中。

      Person p2 = p; //p引用变量只存放了对象的引用地址,这里只是把p保存的地址赋给了p2

     

      3、对象的this引用

      this关键字总是指向调用该方法的 对象(实例,它的最大作用是:让类中的一个方法,调用类中的另一个方法或属性。

      static修饰的方法中不能使用 this,因为它是静态成员(属于类共有,类调用时没有创建实例),无法指向合适的对象(静态成员不能直接访问非静态成员)。

    public class Dog{
        public void jump(){
            System.out.println("执行jump方法");
        }
        public void run(){
            this.jump(); //jump(); //java允许一个对象直接调用另一个对象,一般情况下this可以省略
            System.out.println("执行run方法");
        }
    }

       this与super的区别是什么?

      this 总是指向调用该方法的 对象(实例),它的最大作用是:让类中的一个方法,调用类中的另一个方法或属性。

      this的用法在java中大体可以分为3种:

      1.普通的直接引用,this相当于是指向当前对象本身。

      2.形参与成员名字重名,用this来区分,即如果方法(构造器)里有一个局部变量属性同名,但要在方法里访问这个被覆盖的属性,则必须用this前缀。

      3.引用本类的构造函数。

     

      super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

      super也有三种用法:

      1.普通的直接引用:与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。

      2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分;

      3.引用父类构造函数:

        super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。

        this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。

     

      super() 和 this() 的区别是:

        super() 在子类中调用父类的构造方法,this()本类内调用本类的其它构造方法。

        super()this() 均需放在构造方法内第一行。

        尽管可以用 this 调用一个构造器,但却不能调用两个。

        this 和 super 不能同时出现在一个构造函数里面。因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,

        所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

        this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量、static方法、static语句块。

        从本质上讲,this 是一个指向本对象的指针, 然而 super 是一个Java关键字。

    5.2、 方法详解

      Java语言里,方法不能独立存在,方法必须属于类或对象。一旦将一个方法定义在某个类的类体内,如果这个方法使用了static 修饰,则这个方法属于这个类,否则这个方法属于这个类的 实例

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

      

      形参长度可变的方法:从JDK 1.5之后,Java允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三点(...),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。

      注意事项:

        1、一个方法中最多 只能包含一个 个数可变的形参;

        2、个数可变的形参 只能处于形参列表的最后;

        3、个数可变的形参 本质就是一个数组类型,因此调用时,该形参既可以传入多个参数,也可以传入一个数组。

    public static void test(int a, String ... books); //以可变个数形参定义方法,只能有一个,只能处于形参列表最后,传值时可以传数组
    public static void test(int a, String[] books); //效果同上
    
    test(5, "语文", "数学");  //可变个数形参方法赋值,调用方法时根据简洁
    test(5, new String[]{"语文", "数学"}); //赋值,可用于上面两个方法

      方法递归:一个方法体内调用它自身,被称为方法递归。只要一个方法的方法体实现中再次调用了方法本身,就是递归方法。递归一定要向已知方向递归

      方法重载:如果同一个类中包含了两个或两个以上方法的 方法名相同,但形参列表不同,则被称为方法重载。

        /**
         * 递归方法:一个方法体内调用自身,向已知方向递归
         * 示例1:已知一个数列:f(0)=1, f(1)=4, f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)
         * 分析:f(0)、f(1) 已知,求f(10),向小的方向递归,通用公式变为:f(n)=2*f(n-1) + f(n-2) 
         * 即将 n = n-2,使得向f(1)、f(0)方向递归(向已知方向递归)
         */
        public static int fn(int n) {
            if(n==0) {
                return 1;
            } else if(n==1) {
                return 4;
            } else {
                return 2 * fn(n-1) + fn(n-2); //方法中调用它自身,就是方法递归
            }
        }
        
        /**
         * 递归方法:一个方法体内调用自身,向已知方向递归
         * 示例2:已知一个数列:f(20)=1, f(21)=4, f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)
         * 分析:f(20)、f(21) 已知,求f(10),向大的方向递归,通用公式变为:f(n)=f(n+2) - 2*f(n+1)
         * 向f(20)、f(21)方向递归(向已知方向递归)
         */
        public static int fn2(int n) {
            if(n==20) {
                return 1;
            } else if(n==21) {
                return 4;
            } else {
                return fn2(n+2) - 2 * fn2(n+1); //方法中调用它自身,就是方法递归
            }
        }
        
        //方法重载
        public void test(String str) {
            System.out.println("只有一个字符串的test方法");
        }
        
        public void test(String ... books) {
            System.out.println("形参长度可变的test方法");
        }
        
        
        public static void main(String[] args) {
            //递归方法测试
            System.out.println(fn(10)); //输出fn(10)的结果为: 10497
            System.out.println(fn2(10)); //输出fn2(10)的结果为: -3771
            
            TaskEveryTenMin t = new TaskEveryTenMin();
            //执行第一个test方法,打印出:只有一个字符串的test方法
            t.test("aa"); 
            
            //执行第二个test方法,下面三个都打印出:形参长度可变的test方法
            t.test();
            t.test("aa", "bb");
            t.test(new String[] {"aa"}); //想调用第二个方法,又想只传一个字符串参数则调用这个方法,可传一个字符串数组
            
        }
        

    5.3、 成员变量和局部变量

      成员变量:指的是在 类里定义的变量

      局部变量:指的是在 方法里定义的变量

      变量名称建议:第一个单词首字母小写,后面每个单词首字母大写。Java程序中的变量划分如下图所示:

       1、成员变量 无须显式初始化。系统 会在 加载类 或 创建该类的实例 时,自动为成员变量分配内存空间,自动为成员变量指定初始值

       2、局部变量 除形参之外,都必须显式初始化。也就是说,必须先给方法局部变量和代码块局部变量指定初始值,否则不可以访问它们。

       3、局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化。这意味着定义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,

      系统才会为局部变量分配内存,并将初始值保存到这块内存中。与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。

      如果局部变量是基本类型的变量,则直接把这个变量的值保存在该变量对应的内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,

      通过该地址引用到该变量实际引用的对象或数组。栈内存中的变量无须系统垃圾回收,往往随方法或代码块的运行结束而结束。

     

    5.4、 隐藏和封装

      封装实际上有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都需要通过使用Java提供的访问控制符来实现。

      Java提供了4个访问控制符:private、default、protected 、public,提供了4个访问控制级别。Java的访问控制级别由小到大如下图所示:

     

       关于访问控制符的使用,存在如下几条基本原则:

       1、类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才可能考虑使用public修饰。

        除此之外,有些方法只用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰。

       2、 如果某个类主要用做其他类的父类,该类里的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。

       3、希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通常使用public修饰,从而允许在其他地方创建该类的实例

      4、package 语句必须作为源文件的第一条非注释性语句,一个源文件只能指定一个包,即只能包含一条package语句,

        该源文件中可以定义多个类,则这些类将全部位于该包下。如果没有显式指定package语句,则处于默认包下。

      5、import 可以向某个Java文件中导入指定包层次下某个类或全部类,import语句应该出现在package语句(如果有的话)之后、类定义之前。

      6、JDK 1.5以后更是增加了一种静态导入的语法,它用于导入指定类的某个静态成员变量、方法或全部的静态成员变量、方法。

        静态导入使用 import static 语句,静态导入也有两种语法,分别用于导入指定类的单个静态成员变量、方法和全部静态成员变量、方法。

     

      Java的核心类都放在java包以及其子包下,Java扩展的许多类都放在javax包以及其子包下。

      下面几个包是Java语言中的常用包:

       java.lang:这个包下包含了Java语言的核心类,如String、Math、System和Thread类等,使用这个包下的类无须使用import语句导入,系统会自动导入这个包下的所有类。

       java.util:这个包下包含了Java的大量工具类/接口和集合框架类/接口,例如Arrays和List、Set等。

       java.net:这个包下包含了一些Java网络编程相关的类/接口。

       java.io:这个包下包含了一些Java输入/输出编程相关的类/接口。

       java.text:这个包下包含了一些Java格式化相关的类。

       java.sql:这个包下包含了Java进行JDBC数据库编程的相关类/接口。

       java.awt:这个包下包含了抽象窗口工具集的相关类/接口,这些类主要用于构建图形用户界面(GUI)程序。

      java.swing:这个包下包含了Swing图形用户界面编程的相关类/接口,这些类可用于构建平台无关的GUI程序。

     

    5.5、 深入构造器

      构造器最大的用处就是在创建对象时执行初始化。同一个类里具有多个构造器,多个构造器的形参列表不同,即被称为 构造器重载

      使用this调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句。使用this调用重载的构造器时,系统会根据this后括号里的实参来调用形参列表与之对应的构造器。

     

    5.6、 类的继承

      Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类被称为父类,有的也称其为基类、超类。父类和子类的关系,是一种一般和特殊的关系。

      注意:子类只能从父类获得 成员变量、方法和内部类(包括内部接口、枚举),不能获得 构造器和初始化块。

      如果定义一个Java类未显式指定这个类的直接父类,则这个类默认扩展java.lang.Object类。因此,java.lang.Object类 是所有类的父类,要么是其直接父类,要么是其间接父类。

     

      方法重写:子类包含与父类同名方法的现象被称为方法重写(Override),也被称为方法覆盖。可以说子类重写了父类的方法,也可以说子类覆盖了父类的方法。

      方法的重写要遵循“两同两小一大”规则:

        “两同”即方法名相同、形参列表相同;

        “两小”指的是子类方法 返回值类型 应比父类方法返回值类型更小或相等,子类方法声明 抛出的异常类 应比父类方法声明抛出的异常类更小或相等;

        “一大”指的是子类方法的 访问权限 应比父类方法的访问权限更大或相等。尤其需要指出的是,覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。

      当子类覆盖了父类方法后,子类的对象将无法访问父类中被覆盖的方法,但可以在子类方法中调用父类中被覆盖的方法。

      如果需要在子类方法中调用父类中被覆盖的方法,则可以使用super(被覆盖的是实例方法)或者父类类名(被覆盖的是类方法)作为调用者来调用父类中被覆盖的方法。

     

      如果父类方法具有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就是无法重写该方法。

      如果子类中定义了一个与父类private方法具有相同的方法名、相同的形参列表、相同的返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。

      方法重载:主要发生在 同一个类的 多个 同名方法之间,而重写发生在 子类和父类 的 同名方法 之间

      super是Java提供的一个关键字,super用于限定该对象调用它从父类继承得到的实例变量或方法。

     

    5.7、 多态

      Java引用变量有两个类型:一个是 编译时类型,一个是 运行时类型。

      编译时类型 由声明该变量时使用的类型决定运行时类型 由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)。

      多态:相同类型的变量 调用同一个方法时呈现出 多种不同的行为特征,这就是多态。与方法不同的是,对象的实例变量则不具备多态性。

      instanceof 运算符的前一个操作数通常是一个引用类型变量,后一个通常是一个类(或接口,可以把接口理解成一种特殊的类),它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。是则返回true,否则false。

      在使用instanceof运算符时需要注意:instanceof 运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误。

     

    5.8 、继承与组合

      继承是实现类复用的重要手段,但继承带来了一个最大的坏处:破坏封装。相比之下,组合也是实现类复用的重要方式,而采用组合方式来实现类复用则能提供更好的封装性。

      子类扩展父类时,子类可以从父类继承得到成员变量和方法,如果访问权限允许,子类可以直接访问父类的成员变量和方法,相当于子类可以直接复用父类的成员变量和方法。

      为了保证父类有良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:

       1、尽量隐藏父类的内部数据。尽量把父类的所有成员变量都设置成private访问类型,不要让子类直接访问父类的成员变量。

       2、不要让子类可以随意访问、修改父类的方法。父类中那些仅为辅助其他的工具方法,应该使用private访问控制符修饰,让子类无法访问该方法;

      如果父类中的方法需要被外部类调用,则必须以 public 修饰,但又不希望子类重写该方法,可以使用 final 修饰符来修饰该方法;

      如果希望父类的某个方法 被子类重写,但 不希望被其他类自由访问,则可以使用 protected 来修饰该方法。

      3、尽量不要在父类构造器中调用将要被子类重写的方法。

      4、如果想把某些类设置成最终类,即不能被当成父类,则可以使用 final 修饰这个类,例如JDK提供的 java.lang.String 类 和 java.lang.System 类。

      5、除此之外,使用 private 修饰这个类的所有构造器,从而保证子类无法调用该类的构造器,也就无法继承该类。对于把所有的构造器都使用private修饰的父类而言,可另外提供一个静态方法,用于创建该类的实例。

      6、如果需要复用一个类,除把这个类当成基类来继承之外,还可以把该类当成另一个类的组合成分,从而允许新类直接复用该类的public方法。

     

    5.9、 初始化块

      一个类里可以有多个初始化块,相同类型的初始化块之间有顺序:前面定义的初始化块先执行,后面定义的初始化块后执行。

       在 Java 语言中,当实例化对象时,对象所在类的所有 成员变量 首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建象。

       初始化一般遵循3个原则:

      1、静态对象(变量)优先于非静态对象(变量)初始化,静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化多次;

      2、父类优先于子类进行初始化;

      3、按照 成员变量的定义顺序 进行初始化。 即使变量定义散布于方法定义之中,它们依然在任何方法(包括构造函数)被调用之前先初始化;


      加载顺序:

      父类(静态变量、静态语句块)
      子类(静态变量、静态语句块)

      父类(实例变量、普通语句块)
      父类(构造函数)

      子类(实例变量、普通语句块)
      子类(构造函数)

     

     

  • 相关阅读:
    Max Function
    动态语句语法:EXEC\SP_EXECUTESQL语法(转载)
    [转帖]在SQL SERVER中实现RSA加密算法
    [转]C#实现QQ接口软件QQ的HTTP接口协议探究
    用一条sql语句删除表中所相同的记录
    SQL Server存储过程 对数组参数的循环处理
    在WinDBG中, 使用.shell命令来搜索字符串
    SharePoint提供的一些javascript函数
    IP协议里的Time To Live(TTL)
    SharePoint Application Pool的推荐设置
  • 原文地址:https://www.cnblogs.com/mingyue1818/p/14485493.html
Copyright © 2020-2023  润新知