zoukankan      html  css  js  c++  java
  • java面向对象

    1,嵌套类(内部类)nested class(inner class)

    java内部类与c++嵌套类最大的不同就在于是否有指向外部的引用。

    创建一个static内部类的对象,不需要一个外部类对象,不能从一个static内部类的一个对象访问一个外部类对象。

    内部类或嵌套类在类层级上没有限制,内部类可以是私有类。

    2,集合类

    java中的容器类库一共有两种主要类型:Collection和Map

    collection的每个槽内只保存一个元素;map的每个槽内保存的是键值对

    collection包括list,set,Queue,

      list:Arraylist,LinkedList,Vector;

        list:将以特定次序存储元素。ArrayList:擅长随机访问,擅长插入、删除和移动。

        vector:向量类(vector) 实现类似动态数组的功能。创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型也不需预先选定向量的容量,并可以方便地进行查找。对于预先不知或者不愿预先定义数组大小,并且需要频繁地进行查找,插入,删除工作的情况。可以考虑使用向量类。

      set:Hashset,Treeset,linkedHashset;

        set:不含重复的元素。Hashset:使用散列函数;TreeSet:使用红黑树。linkedHashset:使用链表结构的散列函数。

      queue:Priorityqueue;

        queue:先进先出的容器。

    Map包括HashMap,HashTable,TreeMap

      java中的map都不在重复的key。

       hashmap:线程不安全,适合在map中插入、删除和定位元素。没有分类或者排序,它允许一个null键和多个null值。

       hashtable:线程安全。安全一般意味着效率低,同步。它不允许null键和null值。

       Treemap:适合按自然顺序或自定义顺序遍历键,通常比Hashmap速度慢,在需要排序的map时才使用。

    3,构造函数和析构函数

    重写(覆盖),重载

    覆盖又叫重写,因此重写和覆盖是一个概念。它是覆盖了一个方法并且对其重写,以求达到不同的作用。形式有:对接口方法的实现,在继承中也可能会在子类覆盖父类中的方法。

    重载:它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。

    举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Parent{//定一个类
       public void read(){
       }
       public void show(){//重载了show方法
       }
       public void show(int a){//重载了show方法,比第一个方法多了一个参数
       }
    }
     
    public class Son extends Parent{//son类继承父类parent
       public void read(){//覆盖了父类的read方法。
       }
    }
    重载构造方法是可以的。
    但是重写则不可以,因为被重写的前提是被继承,而构造方法根本就不能被继承,所以谈不上被重写。

    构造函数的格式

      了解了构造函数的基本概念,现在来写一个构造函数,希望大家可以了解、记忆其格式,通过实例发现其与普通函数的不同之处。

    01  public class Demo{

    02     private int num=0;

    03     //无参构造函数

    04     Demo()

    05    {

    06      System.out.println("constractor_run");

    07    }

    08     //有参构造函数

    09     Demo(int num)

    10    {

    11      System.out.println("constractor_args_run");

    12    }

    13     //普通成员函数

    14    public void demoFunction()

    15    {

    16      System.out.println("function_run");

    17    }

    18  }

    构造函数与普通函数的区别

      下面来详细的分析下构造函数与普通函数的区别,通过两者的一个对比,希望可以加深对构造函数的概念的理解。

        1)  格式不同:

          构造函数不存在返回类型,函数名与所在类的类名一致;

          普通函数有返回类型,函数名可以根据需求进行命名。

        2)调用时期不同

          构造函数在类的对象创建时就运行;

          普通函数在对象调用时才会执行。

        3)执行次数不同

          一个对象创建后,其构造函数只执行一次,就是创建时执行;

          一个对象创建后,其普通函数可以执行多次,取决于对象的调用次数。

    java派生类被构造时一定先调用父类的构造函数

    子类可以通过super关键字来显式地调用父类的构造函数。

    当父类没有提供无参数的构造函数时,子类的构造函数中必须显式的调用父类的构造函数;

    如果父类提供了无参数的构造函数,此时子类的构造函数就可以不显式的调用父类的构造函数,默认调用父类的无参构造函数。

    [java] view plain copy
     
    1. package com.bjut.StudyTest;  
    2.   
    3. class Person {  
    4.     public Person() {  
    5.         System.out.println("Base has no args.");  
    6.     }  
    7.   
    8.     public Person(String temp) {  
    9.         System.out.println("Base:" + temp);  
    10.     }  
    11. }  
    12.   
    13. class Student extends Person {  
    14.     public Student() {  
    15.         super("a");  
    16.         System.out.println("Student has no args.");  
    17.     }  
    18.   
    19.     public Student(String temp) {  
    20.         System.out.println("Student:" + temp);  
    21.     }  
    22. }  
    23.   
    24. public class TestConstruction {  
    25.   
    26.     public static void main(String[] args) {  
    27.         Person p = new Student(); // 先调用父类的构造函数,显示调用指定的父类构造函数。  
    28.         Student s = new Student("b"); // 先调用父类的构造函数,默认调用父类无参构造函数。  
    29.     }  
    30.   
    31. }  

    输出:

    [plain] view plain copy
     
    1. Base:a  
    2. Student has no args.  
    3. Base has no args.  
    4. Student:b  


    当有父类时,在实例化对象时会先执行父类的构造函数,然后执行子类的构造函数。

    (补充)java 程序初始化工作执行的顺序:

         父类静态变量 -> 父类静态代码块 -> 子类静态变量 -> 子类静态代码块

     -> 父类非静态变量 -> 父类非静态代码块 -> 父类构造函数

     -> 子类非静态代码块 -> 子类非静态变量 -> 子类构造函数

    注意:constructor在一个对象被new时执行。

    public class Z extends X{//步骤1:先调用X,再调用Z。

      Y y=new Y();//步骤2:接着构造Y

      Z(){//步骤3:最后构造自身Z

        System.out.print("z");
      }

      public static void main(String[] args){

        new Z();

      }

    }

    析构函数在java中常见的就是finalize()函数,GC垃圾回收机制,需要程序员进行重写。

     浅拷贝,即在定义一个类A,使用类似A obj;  A obj1(obj);或者A obj1 = obj; 时候,由于没有自定义拷贝构造函数,C++编译器自动会产生一个默认的拷贝构造函数。这个默认的拷贝构造函数采用的是“位拷贝”(浅拷贝),而非“值拷贝”(深拷贝)的方式,如果类中含有指针变量,默认的拷贝构造函数必定出错。

    用一句简单的话来说就是浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

    多态

     

    多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
    实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
    多态的作用:消除类型之间的耦合关系。

     多态有两种表现形式:重载和覆盖

    java中的动态绑定和静态绑定(或者叫做前期绑定和后期绑定)

    我们发现java属于后期绑定。在java中,几乎所有的方法都是后期绑定的,在运行时动态绑定方法属于子类还是基类。但是也有特殊,针对static方法和final方法由于不能被继承,因此在编译时就可以确定他们的值,他们是属于前期绑定的。特别说明的一点是,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是有JVM来实现的,我们不用去显式的声明它,而C++则不同,必须明确的声明某个方法具备后期绑定。

    java当中的向上转型或者说多态是借助于动态绑定实现的,所以理解了动态绑定,也就搞定了向上转型和多态。

    对于java当中的方法而言,除了final,static,private和构造方法是前期绑定外,其他的方法全部为动态绑定。而动态绑定的典型发生在父类和子类的转换声明之下:
    比如:Parent p = new Children();

    与方法不同,在处理java类中的成员变量(实例变量和类变量)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)。
    Java代码 

    [java] view plain copy

    1. public class Father {  
    2.     protected String name = "父亲属性";  
    3. }  
    4.   
    5.   
    6. public class Son extends Father {  
    7.     protected String name = "儿子属性";  
    8.   
    9.     public static void main(String[] args) {  
    10. 10.         Father sample = new Son();  
    11. 11.         System.out.println("调用的属性:" + sample.name);  
    12. 12.     }  

    13. }  


    结论,调用的成员为父亲的属性。
    这个结果表明,子类的对象(由父类的引用handle)调用到的是父类的成员变量。所以必须明确,运行时(动态)绑定针对的范畴只是对象的方法
    现在试图调用子类的成员变量name,该怎么做?最简单的办法是将该成员变量封装成方法getter形式
    代码如下:
    Java代码 

    [java] view plain copy

    1. public class Father {  
    2.     protected String name = "父亲属性";  
    3.   
    4.     public String getName() {  
    5.         return name;  
    6.     }  
    7. }    
    8.   
    9. public class Son extends Father {  
    10. 10.     protected String name = "儿子属性";  
    11. 11.   
    12. 12.     public String getName() {  
    13. 13.         return name;  
    14. 14.     }  
    15. 15.   
    16. 16.     public static void main(String[] args) {  
    17. 17.         Father sample = new Son();  
    18. 18.         System.out.println("调用的属性:" + sample.getName());  
    19. 19.     }  

    20. }  


    结果:调用的是儿子的属性
    java因为什么对属性要采取静态的绑定方法。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态,多态是java的一大特色。多态也是面向对象的关键技术之一,所以java是以效率为代价来实现多态这是很值得的

  • 相关阅读:
    自定义注解!绝对是程序员装逼的利器!!
    我女儿说要看雪,但是我家在南方,于是我默默的拿起了键盘,下雪咯。
    零基础转行前端,如何拿下了字节跳动、美团等大厂offer
    “TensorFlow 开发者出道计划”全攻略,玩转社区看这里!
    web面试必问的题
    Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)
    520了,用32做个简单的小程序
    虚拟机找不到本机vmnet0,vmnet8,无法连接xshell,解决方案
    Linux用户登出之后保持后台进程(nohup)
    Debian取消从光盘安装软件的方式(please insert the disc labeled)
  • 原文地址:https://www.cnblogs.com/smuxiaolei/p/7716137.html
Copyright © 2011-2022 走看看