Java基础---面向对象(面向对象,类,对象,匿名对象,封装,构造函数,构造代码块,this,static,main,帮助文档,静态代码块,单例)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yecjl/article/details/45064203

一. 面向对象概念:

面向对象其实是一种思考的思想,早期思想是面向过程。
面向过程注重的是过程,过程所涉及的行为,也就是功能。

【实例】:面向过程: 1. 把冰箱打开 2. 把大象放入 3. 冰箱关起来
     面向对象: 打开冰箱,储存,关闭都是对冰箱的操作,是冰箱的行为。冰箱就是一个对象,所以只要操作冰箱所具备的功能,都要定义在冰箱中。

冰箱存储

【总结】: 所属在冰箱中,先看到冰箱,有了冰箱就有了这些功能,把这些功能封装在冰箱中。
      先找到这些功能有哪些特性,所属于什么事物,找到事物,把功能全封装进去。

【面向对象概念】

  1. 面向对象是思考问题的一种思考方式,是一种思想。
  2. 面向对象的好处:将复杂的事情变简单了,只要面对一个对象就行。

【面向对象设计】

面向对象设计把握一个重要的经验:谁拥有数据,谁对外提供操作这些数据(私有)的方法!
(被动的一方是数据的拥有者,主动的一方是执行者)

【实例 1】:人在黑板上画园:
     => 对象:person, blackboard, circle

draw() //画圆
{
    (x, y)  //圆心
    radius  //半径
}

/*圆心和半径是circle内部的数据,画圆的方法要操作圆心和半径
  所以画圆的方法是圆提供的。
*/



【实例 2】:列车司机紧急刹车
     => 对象:司机, 列车
     => 刹车的动作:列车身上的方法


【实例 3】:售票员统计收获小票的金额
     => 对象:售票员, 小票
     => 统计收获小票的金额的方法:是小票身上的方法。
      因为商品的价格是在小票内部的数据,售票员只是调用小票的getTotalMoney( )


【实例 4】:你把门关上了:[ 名称提炼法 ]
     => 对象:门, 人
     => 关门的方法:是门身上的方法,人只是调用门的closeDoor( )

closeDoor() //关门
{
    //旋转; 装到门框; 伸出锁舌; 插到锁孔;
}



【实例 5】:路上有很多汽车,路上还随时可以增加,减少汽车:
     => 对象:路, 汽车
     => 增加,减少汽车的方法:是路身上的方法,不是汽车自己冲过去的。


【实例 6】:球从一根绳子的一端移动到了另一端:
     => 对象:球, 绳子
     => 绳子:为球的移动指引了方向 nextPoint( )
      球:move( )

class Rope
{
    private Point start;
    private Point end;
    public Rope(Point start, Point end)
    {
        this.start = start;
        this.end = end;
    }

    public Point nextPoint(Point currentPoint)
    {
        /*通过两点一线的数学公式可以计算出当前点的下一个点
         * 如果当前点是终点,则返回null;
         * 如果当前点不是线上的点,则抛异常。
         * */
    }
}

class Ball
{
    private Rope rope;
    private Point currentPoint;
    public Ball(Rope rope, Point currentPoint)
    {
        this.rope = rope;
        this.currentPoint = currentPoint;
    }


    public void move() //设置为定时器,每隔1秒move一次
    {
        currentPoint = rope.nextPoint(currentPoint);
        System.out.println("小球移动到了" + currentPoint);
    }
}



【实例 7】:两块石头磨成一把石刀,石刀可以砍树,砍成木材,木材可以做成椅子:
     => 对象:stone  (自己变成石刀,自己都没有了,所以不是自己的方法)
         stoneknife => stoneKnife = KnifeFactory . createKnife( Stone first, Stone second )
         tree
         material  => material = stoneKnife . cut( tree )
         chair    => chair = ChairFactory . createChair( material )

【面向对象的三个特征】

面向对象的三个特征:封装,继承,多态。

开发时:找对象,建对象,用对象,并维护对象之间的关系。

二. 类与对象的关系:

1. 类与对象的关系

  1. 类就是对现实生活中事物的描述。
  2. 对象就是这类事物,实实在在的个体。
【分析】:张三,李四 【映射到内存中】:
现实生活中的对象:张三,李四。

对象:在Java中用new操作符所产生的一个实体,这个实体在堆内存中。

一个实体(具体对象)
想要描述:提取对象中共性内容,对具体的抽象

描述时,这些对象的共性有:姓名,年龄,性别,学习Java的能力。
一个类(class定义的类)


【实例】:需要描述汽车,描述事物 其实就是描述事物的属性行为

  1. 【描述事物】:定义类
  2. 【属性和行为】:类中的成员(成员变量,成员函数)
  3. 【属性】:类中的变量
  4. 【行为】:类中的函数

描述汽车

public class CarDemo {

    public static void main(String[] args) {
        //生产汽车,在Java中通过new操作符来完成,其实就是在堆内存中产生一个实体。‘

        Car c = new Car(); 
        //c 是引用类型变量,就是类类型变量,指向对象,即指向该类产生的实体。
    }

}

class Car
{
    String color = "red"; //描述颜色
    int num = 4; //描述轮胎数

    /**
     * 运行行为
     */
    public void run() 
    {
        System.out.println(color + "..." + num);
    }
}

2. 对象的内存分配

【实例 1】对象的内存分配情况一。

对象的内存分配1

【分析】:

  1. [ new Car( ) ]:新建一个实体。
  2. 堆内存中的变量都有一个特点:默认初始化值。
    如果c.color没有显示初始化值,它的默认初始化值为null(因为字符串默认初始化值为null)。

【实例 2】对象的内存分配情况二。

对象的内存分配2

【分析】:

  1. [ Car c1 = c ] :叫多引用指向同一对象
  2. [ c1.run( ) ]:结果为 color = “green”;  num = 5;
【总结】:
1. 对象的特点:用于封装数据(属性,行为)

2. 类不独立运行可以没有主函数,在主函数中建立类的对象,可以调用该类中的属性方法。

3. 【成员变量与局部变量的区别】: (1)作用不一样: 成员变量:作用于整个类
局部变量:作用于函数中,或者语句中

(2)在内存中的位置: 成员变量:在堆内存中,因为对象存在才在内存中存在。
局部变量:存在栈内存中


class Car
{
    String color = "red"; //描述颜色
    int num = 4; //描述轮胎数  【?】

    /**
     * 运行行为
     */
    public void run()
    {
        System.out.println(color + "..." + num); //成员变量作用在整个类中,可以直接访问。
    }

    /**
     * 在本类中创建本类对象
     */
    public static void main(String[] args)
    {
        Car c = new Car();
        c.num = 10;  //这个变了,但是【?】处没变,相当于car变了,图纸没变。
    }
}

3. 匿名对象

对象可以起名字,也可以不起名字。匿名对象是对象的简写形式。

【匿名对象的使用方式】:
【方式 1】 (1)当对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。

【实例】:

   Car c = new Car( );
   c.num = 5;

 => new Car( ).num = 5;

(2)如果对一个对象进行多个成员调用,必须给这个对象起名字。

【实例】:内存泄露
匿名对象的内存分配
【方式 2】 匿名对象可以作为实际参数进行传递。


【实例】:汽车修配厂,对汽车进行改装,将来的汽车都改成黑色,三个轮胎。

1. [ 法一 ]:没有采用匿名对象。
对象的内存分配

【分析】:show 运行结束完,对象c 还被main引用,当mian结束才变为垃圾。要提前变为垃圾:c = null;

2. [ 法二 ]:采用匿名对象。
匿名对象的内存分配

【分析】:

 (1)[ show( Car c){ } ]:相当于Car c = new Car( ); 堆内存中的new Car( )不是垃圾,被show中的c 引用。

 (2)当show运行结束后,show为垃圾,实际参数的匿名对象没有引用,也变为垃圾。


三. 封装:

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

【封装体】: 函数就是最小的封装体,类也是封装体,包,框架(用于组件的开发)等。
【好处】: 将变量隔离,便于使用,提供重用性,提高安全性。
【原则】: 将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。
【private】: 私有,权限修饰符:用于修饰类中的成员(成员变量, 成员函数),私有只有在本类中有效。


【实例 1】:将年龄私有化

public class Demo {

    public static void main(String[] args) {
        Person p = new Person();
//      p.age = -20;  //访问不到
        p.speak();

    }
}
class Person
{
    private int age;

    public void speak()
    {
        System.out.println("age=" + age);
    }
}

【实例 2】:将age私有化以后,类以外即使建立对象也不能直接访问,但是人应该有年龄,就需要在Person类中提供对应访问age的方法。

  1. set返回值类型肯定是void,只设置不需要返回值,带参数。
  2. get没有参数,只获取,而且get的返回值类型和获取的变量一样。
  3. 一个类里面在setXXX,getXXX功能,代表这个类里边一定有一个私有的属性XXX。
public class Demo {

    public static void main(String[] args) {
        Person p = new Person();
        p.setAge(20);
        p.speak();

    }
}
class Person
{
    private int age; //一个成员变量,通常会对应两个访问方式,一个叫set设置,一个叫get获取。

    public int getAge() {
        return age;
    }

    /*两个访问方式,之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断等语句,
    对访问的数据进行操作,提高代码健壮性。
    */
    public void setAge(int age) {
        if(age > 0 && age < 120)
        {
            this.age = age;
        }
    }

    public void speak()
    {
        System.out.println("age=" + age);
    }
}


setAge访问方式的内存分配

【注意】:封装不是私有,私有仅仅是封装的一种表现形式,不私有也可以实现封装,只要权限在访问不到的范围内是封装的。
     私有权限是最小的权限。

类中有多少属性,一般都需要隐藏起来。

四. 构造函数:

1. 构造函数

构造函数可以重载。
【特点】: 1. 函数名与类名相同。
2. 不用定义返回值类型(与void不同,void是一种返回值类型,代表没有具体结果返回的情况)
3. 不可以写return语句,系统也不会加。


public class Demo {

    public static void main(String[] args) {
        Person p = new Person(); //[1]
    }
}

class Person
{
    public Person() //[2]
    {
        System.out.println("Person");
    }
}
/*
输出结果:Person
[1]和[2]处是一样的。
所以对象一建立,就会调用与之对应的构造函数,new一个就调用一次。
*/
【构造函数的作用】: 可以给对象进行初始化,也就是说当在堆内存中产生对象的时候,这个对象需要一个初始化动作。
【对象为什么要初始化】: 现实生活中的事物很多,它只要一出现,就具备基本的特性,
比如,产生一个人,他一出生就会哭,哭是人一出生的行为。
【构造函数的小细节】: 当一个类中没有定义构造函数时,那么系统会默认给改类加入一个空参数的构造函数:Person(){ };
加一个后,方便该类初始化,否则对象创建不出来。
在类中自定义了构造函数后,默认的构造函数就没有了。


【实例 1】:一个类能产生对个对象,而这些对象都可以去调用不同的初始化方式。

public class Demo {

    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person("lisi");
        Person p3 = new Person("wangwu", 10);

    }
}

class Person
{
    private String name;
    private int age;

    /*自定义了构造函数后,没有手动设置空参数的构造函数初始化,new Person()是不可以创建对象的。*/
    public Person()
    {
        System.out.println("A: name=" + name + ", age=" + age);
    }
    public Person(String name)
    {
        this.name = name;
        System.out.println("B: name=" + name + ", age=" + age);
    }
    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
        System.out.println("C: name=" + name + ", age=" + age);
    }
}

【实例 2】:构造函数和一般函数的区别

public class Demo {

    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person("lisi");
        Person p3 = new Person("wangwu", 10);
        p1.setAge(10); //一般函数,被对象调用。
    }
}
【构造函数和一般函数的区别】:
1. 构造函数和一般函数在写法上有不同
2. 运行上有不同 构造函数:是在对象一建立就运行,给对象初始化。
一般函数:是对象调用才执行,给对象添加对象具备的功能。

3. 运行次数不同 构造函数:一个对象建立,构造函数只运行一次。
一般函数:可以被对象调用多次。

【什么时候定义构造函数?】:
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中,
构造函数在定义时,需不需要未知内容参与运算,需要就定义参数,和函数一样。

【构造函数 + private】:
构造函数是可以被私有化的,因为私有化是来修饰成员的,构造函数也是成员,把构造函数私有化,代表这个类是不可能创建对象的,因为对象都不能进行初始化动作。


2. 构造代码块

构造代码块:定义不同对象共性初始化内容。

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

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


public class Demo {

    public static void main(String[] args) {
        Person p = new Person();
    }
}
class Person
{
    private String name;
    private int age;

    {
        System.out.println("构造代码块");
    }

    public Person()
    {
        System.out.println("name=" + name + ", age=" + age);
    }
}

/*【运行结果】:
   构造代码块
   name=null, age=0
*/

五. this关键字:

【第一点】: this:看上去是用于区分局部变量和成员变量同名情况。

【1. this为什么可以解决这个问题? 2. this到底代表的是什么?】

=> this就代表本类的对象,this代表它所在函数所属对象的引用。


【实例 1】:同名局部变量和成员变量用this

public class Demo {

    public static void main(String[] args) {
        Person p = new Person("张三");
    }
}
class Person
{
    private String name;

    public Person(String name)
    {
        this.name = name; //局部中有name,在局部中找name使用,局部中没有,就找成员用。
    }
}
/* this.name = name; main 中 Person p = new Person("张三");此处的this就是p对象。
    简单说,哪个对象在调用this所在函数,this就代表哪个对象。
*/

【实例 2】:this省略情况

class Person
{
    private String name;
    private int age;

    public void speak()
    {
        System.out.println("name=" + this.name + ", age=" + this.age);
        //此处this可以省略,但变量出现同名情况,就一定要加。
    }
}
/*在一个类中,成员之前相互调用,其实都是对象完成的,而本类中调用this,运行的对象,才是被this表示的对象。*/


【第二点】: this:只能用在构造函数间,一般函数不能用。

【this 的应用】:

(1)当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
   但凡本类功能内部使用了本类对象都用this表示。[ 见实例 1 ]

(2)this 语句:用于构造函数之间的相互调用,用时要传相对应的参数。[ 见实例 2 ]


【实例 1】:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人。

class Person
{
    private int age;

    public Person(int age)
    {
        this.age = age;
    }

    /**
     * 比较两人年龄是否相同
     * @param p 传递对象
     * @return 返回boolean值
     */
    public boolean compare(Person p)
    {
        return this.age == p.age;
    }
}

public class Demo {

    public static void main(String[] args) {
        Person p1 = new Person(10);
        Person p2 = new Person(20);
        System.out.println(p1.compare(p2)); //p1 和 this是引用对象指向同一对象
    }
}

【实例 2】:构造函数之间的相互调用

class Person
{
    private String name;
    private int age;

    public Person(String name)
    {
        this.name = name;
    }

    public Person(String name, int age)
    {
        this(name); //this代表对象,=>p(name) =>new Person(name);给某个对象进行另外的初始化,操作统一对象。
        this.age = age;
    }
}

public class Demo {

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


【第三点】: this:只能定义在构造函数的第一行。(初始化动作要先做)


【实例 1】:this定义在构造函数第一行。

class Person
{
    private String name;
    /* 一个类中有很多种初始化方式,内部初始化方式可以私有起来,只对外暴露一个。*/
    private Person()
    {
    }

    Public Person(String name)
    {
        this();
        this.name = name;
    }
}

【实例 2】:死循环

class Person
{
    private String name;

    Public Person()
    {
        this("haha");
    }

    Public Person(String name)
    {
        this();
        this.name = name;
    }
}

六. static关键字

1. static(静态)关键字

【用法】: (1)是一个修饰符,用于修饰成员(成员变量,成员函数) static的内存分配

【分析】:static静态修饰的内容,被对象所共享。
     static String country = “cn”;被提取出来,不放在堆内存中,每个对象都可以访问。

(2)当成员被静态修饰后,就多了一个调试方法,除了可以被对象调用外,还可以直接被类名调用
   用法:类名 . 静态成员

   [e.g.] Person p = new Person();
       System.out.println(p.country); //有对象
     =>System.out.println(Person.country); //没有对象

【注意】:要区分什么是共享数据,什么不共享,不建议定义多个static。

     [e.g.] 饮水机共享,杯子每个人特有不共享。特有内容随对象存储。

【特点】: (1)随着类的加载而加载:

   也就是说,静态会随着类的消失而消失,说明它的生长周期最长。
   当类一加载到内存中,已经加载在内存中了。

  【实例】:
   class Person
   {
     private String name;
     //【非静态成员变量,实例变量】:对象存在该变量存在;对象不存在,该变量就不存在。

     static String country = “cn”;
     //【静态成员变量,类变量】:当它存在时,对象还没存在,所以调用时类来调用。
   }

(2)优先于对象存在:

   明确一点:静态是先存在的,对象是后存在的。

(3)被所有对象所共享

(4)可以直接被类名所调用

【实例变量和类变量的区别】: (1)存放位置 实例变量:随着对象的加载而存在于堆内存中
类变量:随着类的加载而存在于方法区中

(2)生命周期 实例变量:随着对象的消失而消失
类变量:随着类的消失而存在消失,生命周期最长

【使用注意事项】: (1)静态方法只能访问静态成员。非静态方法既可以访问静态也可以访问非静态。

  【实例】:
   public static void show()
   {
     //System.out.println(this.name); //[错误]
     //this.speak(); //[错误]
   }
   public void speak()
   {
     System.out.println(this.name); //[正确]
     show(); //[正确]
   }

(2)静态方法中不可以定义this, super关键字。
   因为静态优先存在于对象存在,所以静态方法中不可以出现this, super。
(3)main主函数是静态的。

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

【什么时候使用静态?】: (1)什么时候定义静态变量(类变量):

   当对象中出现共享数据值时,该数据被静态所修饰,
   对象中的特有数据(姓名是共享属性而不是共享数据)要定义在非静态中,存在于堆内存中。

(2)什么时候定义静态函数:

   当功能内部没有访问到非静态数据(对象特有数据),那么该功能可以定义成静态的。


2. 主函数 main

public static void main(String[] args)
主函数是一个特殊的函数,可以被JVM调用,作为程序入口。是固定格式。

【主函数的定义】: public: 代表着该函数访问权限是最大的。(被JVM访问)
static: 代表主函数随着类的加载就已经存在了。
void: 主函数没有具体的返回值。
main: 不是关键字,但是是一个特殊的单词,可以被JVM识别。
函数参数(String[] args): 参数类型是一个数组,该数组中的元素是字符串,也称字符串类型数组


【实例】:测试主函数参数长度。main可以重载,但入口只有一个。

public class Demo {
    public static void main(int x) { //可以重载

    }

    public static void main(String[] args) { //程序的入口,先运行,【args参数名】:可以更改,全称:arguments
        System.out.println(args.length); //结果为0

    }
}
/*引用类型数据,能接收两种值:null,和具体值
1. String[] args = null;
2. String[] args = new String[2];*/


JVM在调用主函数时,传入的是new String[0],长度为0。

Javac: 启动底层编译器。
Java: Java对外提供的功能,启动底层的JVM,JVM执行Demo这个类,类中的方法被JVM调用。
设置主函数的参数: java Demo haha hehe heihei:将类后面跟的这些数据,自动的存入数组中,即定义长度为3的数组。
【Demo 】:类名
【haha hehe heihei】:主函数的参数,角标从0开始。


【实例】:JVM调用Demo的主函数,Demo调用DemoTest的主函数。实现在程序中设置主函数的参数。

public class Demo {

    public static void main(String[] args) { 
        String[] arr = {"x", "y", "z"};
        DemoTest.main(arr);
    }
}
public class DemoTest {

    public static void main(String[] args) { 
        for(int i = 0; i < args.length; i++) //打印主函数参数列表
        {
            System.out.println(args[i]);
        }
    }
}

3. 静态的应用

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

【实例】:将操作数据的功能抽取,放入ArrayTool类中。

 虽然可以通过建立ArrayTool对象使用这些工具方法,对数组进行操作。

 [发现了问题]:

  1. 对象是用于封装数据的,可是ArrayTool对象并没有封装特有数据。

  2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
    => 所以这时就考虑,让程序更严谨,是不需要对象的。
      可以将ArrayTool中的方法都定义成static的,直接通过类名调用。

  3. 将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。
    => 所以为了更加谨慎,强制让该类不能建立对象,避免堆内存中出现乱七八糟对象。
      可以将构造函数私有化完成。
public class Demo {

    public static void main(String[] args) {
         int[] arr = {5,1,6,4,2,8,9};
         System.out.println("-------------排序前-------------");
         ArrayTool.printArray(arr);
         ArrayTool.selectSort(arr); //选择排序
         System.out.println("-------------排序后-------------");
         ArrayTool.printArray(arr);

    }
}

/**
这是一个可以对数组进行操作的工具类,有获取最值,排序等功能。
@author 佳露
@version V1.1
*/
public class ArrayTool
{
    /**
     * 私有化空参数构造函数,不能建立实体对象,无法实例化
     */
    private ArrayTool(){}

    /**
     * 获得数组最大值max
     * @param arr 数组
     * @return 最大值
     */
    public static int getMax(int[] arr)
    {
        int max = arr[0];
        for (int i=1; i<arr.length; i++)
        {
            if(max < arr[i])
                max = arr[i];

        }

        return max;
    }

    /**
     * 获得数组最小值min
     * @param arr 数组
     * @return 最小值
     */
    public static int getMin(int[] arr)
    {
        int min = arr[0];
        for (int i=1; i<arr.length; i++)
        {
            if(min > arr[i])
                min = arr[i];

        }

        return min;
    }

    /**
     * 直接插入排序 + 希尔排序 + space取整
     * @param arr 数组
     */
    public static void shellSort(int[] arr)
    {
        int space = arr.length;
        do
        {
            space >>= 1; // space = space/2;

            for (int i = space; i < arr.length; i++) {  //在直接排序的基础上,将间隔1,改为space
                if(arr[i] < arr[i-space])
                {
                    int temp = arr[i], j;
                    for (j = i - space; j >= 0 && arr[j] > temp; j -= space) {
                        arr[j + space] = arr[j];
                    }
                    arr[j + space] = temp;
                }
            }

        }while(space > 0);
    }

    /**
     * 直接插入排序 + 希尔排序 + space四舍五入
     * @param arr 数组
     */
    public static void shellSort2(int[] arr)
    {
        double spaceD = arr.length;
        int space;
        do
        {
            spaceD = Math.ceil(spaceD / 2); //四舍五入
            space = (int)spaceD;

            for (int i = space; i < arr.length; i++) {  //在直接排序的基础上,将间隔1,改为space
                System.out.println("arr[i-space="+arr[i-space]);
                System.out.println("arr[i]="+arr[i]);
                if(arr[i] < arr[i-space])
                {
                    int temp = arr[i], j;
                    for (j = i - space; j >= 0 && arr[j] > temp; j -= space) {
                        System.out.println("arr[j]="+arr[j]);
                        arr[j + space] = arr[j];
                    }
                    arr[j + space] = temp;
                    printArray(arr);
                }
            }

        }while(space != 1); //由于四舍五入,最后space一定为1
    }

    /**
     * 直接插入排序
     * @param arr 数组
     */
    public static void insterSort(int[] arr)
    {
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] < arr[i-1])
            {
                int temp = arr[i], j;
                for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
                    arr[j + 1] = arr[j];
                }
                arr[j + 1] = temp;
            }
        }
    }

    /**
     * 冒泡排序
     * @param arr 数组
     */
    public static void bubbleSort(int[] arr)
    {
        for (int i = 0; i < arr.length - 1; i++) {

            int j = 0, max = 0; //max 记录最大值的角标

            for (j = 1; j < arr.length - i; j++) {
                if(arr[j] > arr[max])
                {
                    max = j;
                }
            }
            if(j-1 != max) //如果max 角标不等于最后一个角标,两数交换
            {
                swap(arr, j-1, max); //两数交换
            }
        }
    }


    /**
     * 选择排序
     * @param arr 数组
     */
    public static void selectSort(int[] arr)
    {
        for (int i = 0; i < arr.length - 1; i++) {
            for(int j = i + 1; j < arr.length; j++)
            {
                if(arr[i] > arr[j])
                {
                    swap(arr, i, j); //两数交换
                }
            }
        }
    }

    /**
     * 交换数组中的两个元素
     * @param arr 数组
     * @param i 数组角标i
     * @param j 数组角标j
     */
    private static void swap(int[] arr, int i, int j)
    {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

    /**
     * 打印数组
     * @param arr 数组
     */
    public static void printArray(int[] arr)
    {
        for (int i = 0; i < arr.length; i++) {
            if( i != arr.length -1)
            {
                System.out.print(arr[i] + ", ");
            }
            else
            {
                System.out.println(arr[i]);
            }
        }
    }
}

5. 帮助文档的制作

帮助文档:API文档(应用程序接口)
开始制作程序的说明书,Java的说明书通过文档注释来完成。

【文档注释】: [ @author ]:作者
[ @version ]:版本号
[ @param ]:参数
[ @return ]:返回值

【帮助文档的制作】: javadoc -d myhelp -author -version ArrayTool.java

[ -d ]:当前文件夹下
[ myhelp ]:文件夹名

【类修饰符】: private:私有的不被做成说明文档。
public 和 protect(保护)才被做成说明文档。

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


6. 静态代码块

【格式】: static
{
  静态代码块中的执行语句;
}

【特点】: 随着类的加载而执行,只执行一次。用于给类进行初始化的。
一个类进内存,不需要对象的情况下,做一些事,优先于主函数,优先于对象。


【实例 1】:static代码块执行过程

public class Demo { // [1]类加载进内存

    static // [2]类初始化
    {
        System.out.println("b");
    }

    public static void main(String[] args) {
        new StaticCode();  // [4]类初始化
        new StaticCode();  // [4]类初始化,已经定义了,第二次不再执行。
        System.out.println("over");  // [5]输出执行
    }

    static // [3]类初始化:优先于主函数
    {
        System.out.println("c");
    }

}
class StaticCode
{
    static // [4]类初始化
    {
        System.out.println("a");
    }
}

/*[输出结果]:b c a over*/

【实例 2】:static代码块可以验证类有无加载

public class Demo {

    public static void main(String[] args) {
        //[1] 
        //StaticCode.show(); // 加载了类,[输出结果]: a  show run

        //[2]
        //StaticCode sc = new StaticCode();// 加载了类,用的了构造方法。 [输出结果]: a

        //[3]
        //StaticCode sc = null;// 没有加载类,sc没有指向。 [输出结果]: (没有输出)

        /*用到类中的内容,才加载类。*/
    }

}
class StaticCode
{
    static
    {
        System.out.println("a");
    }

    public static void show()
    {
        System.out.println("show run");
    }
}

【实例 3】:构造函数(有无参数),构造代码块,静态代码块执行过程

public class Demo {

    public static void main(String[] args) {
        StaticCode sc = new StaticCode(4);  // 类加载,调用有参构造函数。
    }

}
class StaticCode 
{
    int num = 9;

    /**
     * 无参构造函数,初始化对应对象
     */
    public StaticCode() 
    {
        System.out.println("b");
    }

    /**
     * 带参数构造函数,初始化对应对象  [3] 对应对象初始化
     */
    public StaticCode(int x) 
    {
        System.out.println("d");
    }

    /**
     * 静态代码块,初始化类  [1] 类初始化
     */
    static 
    {
        System.out.println("a");
    }

    /**
     * 构造代码块,初始化对象  [2] 所有对象初始化
     */
    {
        System.out.println("c" + this.num);
    }
}

/*[输出结果]:a c9 d*/

7. 对象建立,对象调用非静态方法内存分配

【实例】:分析以下代码的内存分配情况

public class Demo {

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

}
class Person
{
    private String name = "haha";
    private int age;
    private static String country = "CN";

    public Person(String name, int age)
    {
         this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void speak()
    {
        System.out.println("name=" + name + ", age=" + age);
    }

    public static void showCountry()
    {
        System.out.println("country=" + country);
    }
}


对象建立过程的内存分配

【对象建立过程步骤】:

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


非静态调用的内存分配

七. 单例设计模式:

设计模式:让问题简单化,解决某一类问题最行之有效的方法。Java有23种设计模式。
单例设计模式:解决一个类在内存只存在一个对象,类似配置文件。

【想要保住对象唯一】:

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

【这三步怎么用代码体现呢?】

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

【注】:对于事物该怎么描述,还是怎么描述。当需要将事物的对象保证在内存中唯一时,就就将以上三步加上即可。

【实例 1】:饿汉式

public class Demo {

    public static void main(String[] args) {
        Single s1 = Single.getInstance();
        Single s2 = Single.getInstance();   
    }

}
/**
 * 【饿汉式】
 */
class Single
{
    private Single(){}
    private static Single single = new Single();
    public static Single getInstance()
    {
        return single;
    }
}


饿汉式的内存分配

【饿汉式特点】:Single类一进内存就已经创建好了对象,这是初始化对象,成为饿汉式。

【实例 2】:懒汉式

public class Demo {

    public static void main(String[] args) {
        Single s1 = Single.getInstance();
        Single s2 = Single.getInstance();   
    }

}
/**
 * 【懒汉式】
 */
class Single
{
    private Single(){}
    private static Single single = null;
    public static synchronized Single getInstance()
    {
        if(single == null)
        {
            single = new Single();
        }
        return single;
    }
}


懒汉式的内存分配

【懒汉式】:对象是方法被调用时,才初始化,也叫做对象的延时加载,称为懒汉式。

【懒汉式的特点】:Single类进内存,对象还没有存在,只有调用getInstance方法时,才建立对象。

【实例】:懒汉式多线程调用

/**
 * 【懒汉式】
 */
class Single
{
    private Single(){}
    private static Single single = null;
    //法一:(低效的)
//  public static synchronized Single getInstance()
//  {
//      if(single == null)
//      {
//          single = new Single();
//      }
//      return single;
//  }

    //法二:(高效的)
    public static Single getInstance()
    {
        if(single == null)
        {
            synchronized(Single.class)
            {
                if(single == null)
                {
                    single = new Single();
                }
            }
        }
        return single;
    }
}

【记住原则】:定义单例,建议使用饿汉式。

猜你喜欢

转载自blog.csdn.net/yecjl/article/details/45064203