Java基础-06-面向对象(续)

Java基础-面向对象

Java编程-面向对象入门(续)

1、面向对象(static关键字)

static(静态)关键字:用于修饰成员(成员变量和成员函数) 被修饰后的成员具备以下特点:

(1)、随着类的加载而加载。
(2)、优先于对象存在。
(3)、被所有对象所共存。
(4)、可以直接被类名所调用。

使用注意:

(1)、静态方法只能访问静态成员。
(2)、静态方法中不可以写this,super关键字。
(3)、主函数是静态的。

Eg:

public class Demo1 
{
    public static void main(String []args)
    {
        Person p1 = new Person();
        Person p2 = new Person();
        p1.name = "张三";
        p2.name = "李四";
        p1.show();
        p2.show();
    }
}

class Person 
{
    String name ;             //成员变量,实例变量
    String country = "CN";
    public void show()
    {
        System.out.println(name+"::"+country);
    }
}

输出结果:

张三::CN
李四::CN

从以上代码可以发现,无论新建多少个新的Person类的对象,对象的国籍都是CN,我们可以考虑使用static关键字,让country给对象所共享,所以我们可以在代码String country = "CN";前加上static关键字。

static关键字用法:是一个修饰符,用于修饰成员(成员变量,成员函数)。当成员被静态修饰后,就多了一种调用方式,除了可以被对象调用外,还可以直接被类名调用:类名.成员变量。例如Person.country;,注意,已经被static修饰过的成员变量已经不在堆内存中了,而存在于一个叫做方法区的地方。如图:

static特点:

(1)、随着类的加载而加载。(也就是说静态会随着类的消失而消失,所以生命周期最长,消耗内存)
(2)、被所有对象所共享。
(3)、优先于对象存在。(明确一点,静态是先存在的,对象是后存在的。)
(4)、可以直接被类名调用。
  • 实例变量和类变量的区别:

    (1)、存放位置:类变量随着类的加载而存在于方法区中,实例变量随着对象的建立而存在于堆内存中。
    (2)、生命周期:类变量生命周期最长,随着类的消失而消失。实例变量的生命周期随着对象的消失而消失。
    
  • 静态使用的注意事项:

    (1)、静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态。
    (2)、静态方法中不可以定义this,super关键字,因为静态优先对象存在,所以静态方法中不可以出现this。
    (3)、主函数是静态的。
    
  • 静态有利有弊:

    (1)、利:对对象的共享数据进行单独空间的存储,节省空间,没必要每一个对象中都存储一份。
    (2)、生命周期过长,访问出现局限性。(静态虽好,只能访问静态。)
    

2、面向对象(main函数)

(1)、主函数是静态的:public static void main(String [] args)

args : 是arguments的缩写,是参数的意思,它是一个变量名,可以改变,这样写是一种习惯而已。
主函数:是一个特殊的函数,作为一个程序的入口,可以被JVM调用。

主函数的定义:
public :代表着该函数访问权是最大的。

static:代表主函数随着类的加载就已经存在了。

void:主函数没有具体的返回值。

main:不是关键字,但是是一个特殊的单词,可以被JVM识别。
(String [] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。

主函数是固定格式的:JVM识别。

Eg:

public class Demo1 
{
    public static void main(String []args)
    {
        System.out.println(args);
    }
}

输出结果:

[Ljava.lang.String;@1db9742

由此可见,JVM在调用主函数时,传入的是new String[0];

3、面向对象(静态什么时候使用)

  • 什么时候使用静态呢?

要从两方面下手(因为静态修饰的内容有成员变量和函数):

(1)、什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰,对象中的特有数据要定义成非静态存在于堆内存中。
(2)、什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。

Eg:

public class Demo1 
{
    public static void main(String []args)
    {
        Person p = new Person();
        p.show();                           //新建对象调用show方法
        Person.show();                 //由于Person类中的show方法已经定义成了静态,所以我们可以类名直接调用。
    }
}

class Person
{
    String name ;
    public static void show ()
    {
        System.out.println("haha");
    }
}

输出结果:

haha
haha

4、面向对象(静态的应用--工具类)

/*静态的应用*/
Eg:
public class Demo1 
{
    public static void main(String []args)
    {
        int [] arr = {3,4,1,8};                //自定义数组
        int max = Test.getMax(arr);           //类名Test直接调用已经用static定义的工具函数getMax
        System.out.println("Max= "+max);    //输出最大数
    }
}

class Test                        //测试工具类
{
    public static int getMax(int []arr)            //用静态来定义好的工具函数getMax
    {
        int max = 0;
        for(int x = 1;x<arr.length;x++)
        {
            if(arr[x]>arr[max])
            {
                max = x;
            }
        }
        return arr[max];    
    }
}

静态的应用: 每一个应用程序中都有共性功能,可以将这些功能进行抽取,独立封装,以便复用。

/*静态的应用2*/
Eg:
public class Demo1 
{
    public static void main(String []args)
    {
        int  [] arr = {3,1,87,32,8};    //自定义一个整数数组
        int max = Tool.getMax(arr);    //用工具类直接调用方法,得到的数组中最大数存入整数max中
        int min = Tool.getMin(arr);    //用工具类直接调用方法,得到的数组中最小数存入整数min中
        Tool.selectSort(arr);            用工具类直接调用方法
        System.out.println(max);
        System.out.println(min);
        for(int x = 0 ; x< arr.length;x++)
        {
            System.out.print(arr[x]+" ");
        }
    }
}

class Tool                        //工具类
{
    private Tool(){};             //私有化构造函数,为了不让用户建立该工具类的对象。
    public static int getMax(int []arr)            //用静态来定义好的工具函数getMax,获取数组中最大数
    {
        int max = 0;
        for(int x = 1;x<arr.length;x++)
        {
            if(arr[x]>arr[max])
            {
                max = x;
            }
        }
        return arr[max];    
    }
    public static int getMin(int[]arr)        //用静态来定义好的工具函数getMin,获取数组中最小数
    {
        int min = 0;
        for(int x = 1;x<arr.length;x++)
        {
            if(arr[x]<arr[min])
            {
                min = x;
            }
        }
        return arr[min];
    }
    public static void selectSort(int []arr)    //用静态来定义好的工具函数selectSort,给数组从小到大排序
    {
        for(int x = 0 ; x<arr.length-1 ; x++)
        {
            for(int y = x+1;y<arr.length;y++)
            {
                if(arr[x]>arr[y])
                {
                    swap(arr,x,y);
                }
            }
        }
    }
    public static void bubbleSort(int []arr)//用静态来定义好的工具函数bubbleSort,用冒泡排序法给数组排序
    {
        for(int x= 0 ; x<arr.length-1;x++)
        {
            for(int y = 0 ; y<arr.length-x-1;y++)
            {
                if(arr[y]>arr[y+1])
                {
                    swap(arr,y,y+1);
                }
            }
        }
    }
    private static void swap(int[]arr,int a ,int b)//用静态且私有来定义好的工具函数swap,交换数组中的两个数
    {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

程序结果:

87
1
1 3 8 32 87 

以上程序在主函数中都是用类名直接调用工具类中的方法,因为工具类中的方法都是用static来定义的,即在对象建立之前就存在,可以直接类名调用,例如Tool.selectSort(arr);,将方法都静态后,可以方便使用,但是该类还是可以被其他程序建立对象的,为了更为严谨,强制让该类不能建立对象,可以通过将构造函数私有化完成,即private Tool(){};

5、面向对象(帮助文档的制作 javadoc.exe)

如果某人共享了一个Tool.class文件在C:/myclass文件夹下,那么我们设置set classpath = .;c:/myclass,接下来,将Tool.class文件发送给其他人只要将该文件设置到classpath路径下,就可以使用该工具类,但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚,因为该类并没有使用说明书。制作java的程序说明书,是通过文档注释来完成的。用上面的程序例子为例,文档注释的方法如下:

Eg:
public class Demo1 
{
    public static void main(String []args)
    {
        int  [] arr = {3,1,87,32,8};    //自定义一个整数数组
        int max = Tool.getMax(arr);    //用工具类直接调用方法,得到的数组中最大数存入整数max中
        int min = Tool.getMin(arr);    //用工具类直接调用方法,得到的数组中最小数存入整数min中
        Tool.selectSort(arr);            用工具类直接调用方法
        System.out.println(max);
        System.out.println(min);
        for(int x = 0 ; x< arr.length;x++)
        {
            System.out.print(arr[x]+" ");
        }
    }
}

class Tool                        //工具类
{
    /**
     *这是一个可以对数组进行操作的工具类,该类中提供了获取数组中最值、排序等功能。
     *@author 张三
     *@version v1.1
     * */
    /**
     * 获取一个整数数组中的最大值
     * @param arr 接收一个int类型的数组
     * @return 会返回一个该数组中的最大值
     * */
    public static int getMax(int []arr)            //用静态来定义好的工具函数getMax,获取数组中最大数
    {
        int max = 0;
        for(int x = 1;x<arr.length;x++)
        {
            if(arr[x]>arr[max])
            {
                max = x;
            }
        }
        return arr[max];    
    }
    /**
     * 获取一个整数数组中的最小值
     * @param arr 接收一个int类型的数组
     * @return 会返回一个该数组中的最小值
     * */
    public static int getMin(int[]arr)        //用静态来定义好的工具函数getMin,获取数组中最小数
    {
        int min = 0;
        for(int x = 1;x<arr.length;x++)
        {
            if(arr[x]<arr[min])
            {
                min = x;
            }
        }
        return arr[min];
    }
    /**
     * 给整数数组进行排序
     * @param arr 接收一个int类型的数组
     * */
    public static void selectSort(int []arr)    //用静态来定义好的工具函数selectSort,给数组从小到大排序
    {
        for(int x = 0 ; x<arr.length-1 ; x++)
        {
            for(int y = x+1;y<arr.length;y++)
            {
                if(arr[x]>arr[y])
                {
                    swap(arr,x,y);
                }
            }
        }
    }
    /**
     * 给整数数组进行冒泡排序
     * @param arr 接收一个int类型的数组
     * */
    public static void bubbleSort(int []arr)//用静态来定义好的工具函数bubbleSort,用冒泡排序法给数组排序
    {
        for(int x= 0 ; x<arr.length-1;x++)
        {
            for(int y = 0 ; y<arr.length-x-1;y++)
            {
                if(arr[y]>arr[y+1])
                {
                    swap(arr,y,y+1);
                }
            }
        }
    }
    /**
     * 交换数组中的两个数
     * @param arr 接收一个int类型的数组
     * @param a 接收数组中的角标值
     * @param b 接收数组中的角标值
     * */
    private static void swap(int[]arr,int a ,int b)//用静态且私有来定义好的工具函数swap,交换数组中的两个数
    {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

然而如何对以上的代码生成程序说明书呢?这需要我们在命令行里输入javadoc -d myhelp -author -version Demo1.java,注意程序里的类必须是public的。如图:

输入命令并生成完毕后,我们就去相应的地址去找程序说明书,如图:

在myhelp文档里这么多的文件,我们只需要点击index这一文件即可,它是一个索引文件,也就是所谓的程序说明书,打开后如图:

  • 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰符,如果类没有被public修饰,那么默认的构造函数,也没有public修饰,默认构造函数的权限是随着类的变化而变化的。

6、面向对象(静态代码块)

静态代码块格式:

static
{
               静态代码块中的执行语句;
}

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

Eg:
class StaticCodeDemo
{
    static                 //静态代码块,输出B
    {
        System.out.println("B");
    }
    public static void main(String []args)
    {
        new StaticCode();
        new StaticCode();                   //这个对象建立时不执行StaticCode类中的静态代码块了,只执行一次!
            System.out.println("OVER");
    }
    static                //静态代码块,输出C
    {
        System.out.println("C");
    }
}

class StaticCode
{
    static                //静态代码块,输出A
    {
        System.out.println("A");
    }
}

程序结果:

B
C
A
OVER

这是因为程序首先执行有主函数的类,所以虚拟机首先加载StaticCodeDemo类,因而首先执行第一个静态代码块,输出B,然后虚拟机读到new StaticCode();时,即新建一个StaticCode类的对象,因此StaticCode类加载,同时也执行类里的静态代码块,输出C,当虚拟机再读到new StaticCode();时,类里的静态代码块不再执行,最后读到最后一个静态代码块时输出A,最后输出OVER。

Eg2:
class StaticCodeDemo
{
    public static void main(String[]args)
    {
        StaticCode.show();            //用静态调用类方法
    }

}

class StaticCode
{
    static                //静态代码块,输出A
    {
        System.out.println("A");
    }
    public static void show()
    {
        System.out.println("show run");
    }
}

结果输出:

A
show run

上例中是用静态调用类方法,在调用的同时也加载了类,因而执行了静态代码块。

Eg3:

class StaticCode
{
    int num = 9;
    StaticCode()        //构造函数
    {
        System.out.println("B");                  //没执行
    }
    static              //静态代码块
    {
        System.out.println("A");
    }
    {                                    //构造代码块
        System.out.println("C"+this.num);
    }
    StaticCode(int x)                //构造函数重载
    {
        System.out.println("D");
    }
    public static void show()                //静态方法
    {
        System.out.println("show run");
    }
    }
    class StaticCodeDemo
    {
    public static void main(String[]args)
    {
        new StaticCode(4);            //调用StaticCode类的构造函数。
    }
}

程序结果:

A
C9
D

从主函数中可以看出,new StaticCode(4);直接调用了StaticCode类中的构造函数,然而先读静态代码块,输出A,然后读构造代码块的内容,输出C9,最后是读取构造函数,输出D。

7、面向对象(对象初始化的过程)

Eg:
class Person
{
    private String name;
    private int age ;
    private static String country = "CN";            //静态定义字符串变量
    {
        System.out.println(name+".."+age);        //构造代码块,输出变量name和age的值
    }
    Person(String name, int age)            //构造函数重载,给name和age初始化
    {
        this.name = name ;
        this.age = age ; 
    }
    public void setName(String name)        //类方法setName,给name赋值
    {
        this.name = name ;
    }
    public void speak()                //类方法,输出当前name和age的值
    {
        System.out.println(this.name+"..."+this.age);
    }
    public static void showCountry()            //类方法showCountry,显示当前对象的国籍
    {
        System.out.println("country = "+country);
    }
}

class PersonDemo
{
    public static void main(String[]args)
    {
        Person p = new Person("ZhangSan",20);        //新建对象p并赋值
        p.speak();                    //调用类方法
    }
}

输出结果:

null..0
ZhangSan...20

以上程序在建立新对象并赋值的同时,先执行Person类里的构造代码块,所以首先输出对象的name和age的默认值null..0,然后在进行初始化,调用speak()方法输出赋值后的name和age值ZhangSan...20。 详细来说明以上代码的运作流程:Person p = new Person("ZhangSan",20); 该句话做了什么事情:

(1)、因为new用到了Person.class,所以会先找到Person.class文件并加载到内存。
(2)、执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
(3)、在堆内存中开辟空间,分配内存地址。
(4)、在堆内存中建立对象的特有属性,并进行默认初始化。
(5)、对属性进行显示初始化。
(6)、对对象进行构造代码块初始化。
(7)、对对象进行对应的构造函数初始化。
(8)、将内存地址赋给栈内存中的p变量。

8、面向对象(对象调用成员过程)

上一个例子的Person类。 然后在PersonDemo类中输入以下代码:

public static void main(String[] args)
{
          Person p = new Person ("ZhangSan",20);
          p.setName = ("lisi");
}

以上代码的示图如下:

9、面向对象(单例设计模式)

设计模式:一种思想,解决某一类问题最行之有效的方法,Java中有23种设计模式,其中一种叫单例设计模式。 单例设计模式:解决一个类在内存只存在一个对象,想要保证对象唯一。

(1)、为了避免其他程序过多建立该类对象,先控禁止其他程序建立该类对象。
(2)、还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
(3)、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

以上三步怎么用代码实现呢?

(1)、将构造函数私有化。
(2)、在类中创造一个本类对象。
(3)、提供一个方法可以获取到该对象。

代码:

Eg1:
class Single
{
    private Single(){}
    private static Single s = new Single();
    public static  Single getInstance()
    {
        return s;
    }
}
class SingleDemo
{
    public static void main(String []args)
    {
        Single s1 = Single.getInstance();
        Single s2 = Single.getInstance();
    }
}

以上代码示图如下:

以下是在Eg1的基础上改动的两个测试例子。

Eg2:
class Single
{
    private int num;        
    public void setNum(int num)  //公共方法setNUm,为私有变量num赋值
    {
        this.num = num ; 
    }
    public int getNum()            //获得当前num的值
    {
        return num ;
    }
    Single(){}        //定义构造函数,默认是公共的
    private static Single s = new Single();        //新建一个私有静态的Single类对象。
    public static Single getInstance()            //静态定义一个Single类型的方法getInstance,返回对象地址。
    {
        return s;
    }
}

class SingleDemo
{
    public static void main(String []args)
    {
        Single s1 =new Single();            //新建一个Single类对象
        Single s2 =new Single();            //新建一个Single类对象
        s1.setNum(30);                        //由于Single类中构造函数没有私有,所以对象s1调用
                                                          //setNum方法给num赋值30并不会影响到s2中的num变量
        System.out.println(s2.getNum());    //因此输出s2中的num是0.因为没赋值。

    }
}

输出结果:0

Eg3:
//由上例出现的问题,我们可以给Single类中构造函数Single(){}私有化,禁止主函数新建对象,那么就不会出现上例中的问题。
class Single
{
    private int num;        
    public void setNum(int num)  //公共方法setNUm,为私有变量num赋值
    {
        this.num = num ; 
    }
    public int getNum()            //获得当前num的值
    {
        return num ;
    }
    private Single(){}        //定义构造函数,并定义为私有
    private static Single s = new Single();        //新建一个私有静态的Single类对象。
    public static Single getInstance()            //静态定义一个Single类型的方法getInstance,返回对象地址。
    {
        return s;
    }
}

class SingleDemo
{
    public static void main(String []args)
    {
        Single s1 =Single.getInstance();            //定义一个Single类的变量,把Single类中自定义对象指向s1
        Single s2 =Single.getInstance();            //定义一个Single类的变量,把Single类中自定义对象指向s2
        s1.setNum(30);                        //由于s1和s2都指向了同一个地址,因此s1调用
                                                          //setNum给num赋值30,s2再调用getNum获得的num也就是30
        System.out.println(s2.getNum());    //输出是30

    }
}

输出结果:30

对于事物该怎么描述,就还是怎么地去描述,当需要将事物的对象保证在内存中唯一时,就将以上单例设计模式的三步加在类中即可。

Eg:
//用单例设计模式方法设计一个Student类、
class Student
{
    private int age;                //定义私有的整型age
    private static Student s = new Student();        //类中建立新的Student类对象s
    private Student(){}            //把构造函数私有化
    public static Student getStudent()           //获得类中对象的地址方法
    {
        return s;
    }
    public void setAge(int age)            //方法setAge为类中的成员变量age赋值
    {
        this.age = age ;
    }
    public int getAge()        //方法getAge返回类中成员变量age
    {
        return age;
    }
}

class SingleDemo
{
    public static void main(String []args)
    {
        Student s1 = Student.getStudent();        //定义Student类变量,把Student类中的对象地址赋值给s1
        Student s2 = Student.getStudent();        //定义Student类变量,把Student类中的对象地址赋值给s2

    }
}

10、面向对象(单例设计模式方法2)

前一节所说的单例设计模式也称为饿汉式,即使先初始化对象。 模板:

class Single 
{
    private static Single s = new Single();
    private Single(){}
    public static Single getInstance()
    {
        return s;
    }
}

而这节所分析的另一种方法,单例设计模式方法2,也称之为懒汉式,即对象是方法被调用时,才初始化,也叫做对象的延时加载。 模板:

class Single 
{
    private static Single s = null;
    private Single(){}
    public static Single getInstance()
    {
        if(s==null)            //即如果s变量不是空,那么就新建一个对象,并把地址赋值给s变量,并返回。
        {
            s = new Single();
        }
        return s;
    }
}

饿汉式和懒汉式的区别: 饿汉式:Single类一进内存,就已经创建好了对象 懒汉式:Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值