Java面向对象讲解与实例

面向对象

文章目录

面向过程和面向对象


面向过程和面向对象都是软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。

面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方法,一步一步,最终完成。

这个适合简单任务,不需要过多协作的情况下。比如,如何开车?

对象的进化史(数据管理和企业管理共同之处)


事物的发展总是遵循“量变引起质变”的哲学原则;企业管理和数据管理、甚至社会管理也有很多共通的地方。

  • 数据无管理时代
    • 例如:初级公司一人身兼多职,但当业务量大时无法管理
  • 数组管理和企业部门制
    • 相同数据放到同一数据中,相当于部门,数组也是对象
  • 对象和企业项目制
    • 对象包含不同类型的变量,还可以有各种方法,相当于独立公司

对象和类的概念


类可以看做是一个模板,或者图纸,系统根据类的定义来造出对象。

类:

我们叫做class。

对象:

我们叫做Object,instance(实例)。

以后我们说某个类的对象,某个类的实例。是一样的意思。

public class Stu{
    /*
    属性filed,成员变量
    方法
    */
    int id;
    String name;
    int age;
    Computer comp;//电脑
    
    void speak(){
        System.out.println("说话");
    }
    
    void study(){
        System.out.println("使用"+comp.brand);
    }
    /*无参的构造方法 用于创建这个类的对象,无参的构造方法可以由系统自动创建*/
    Stu(){
        
    }
    
    public static void main(String[] args){
        /*实例化对象*/
        Stu stu = new Stu();//创建一个对象 通过构造方法来创建类
        stu.id=1;
        stu.name="junwei";
        Computer c1 = new Computer();
        c1.brand = "ThinkPad";
        stu.comp = c1;
        stu.study();
    }
}

class Computer{
    String brand;//品牌
}

面向对象的内存分析


Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area

栈stack的特点:

  1. 栈描述的是方法执行的内存模型。每个方法都被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
  2. JVM为每一个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变变量等)
  3. 栈属于线程私有。不能实现线程间的共享
  4. 栈的存储特性是“先进后出,后进先出”
  5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间

堆heap的特点:

  1. 堆用于存储创建好的对象和数组(数组也是对象)
  2. JVM只有一个堆,被所有线程共享
  3. 堆是一个连续的内存空间,分配灵活,速度慢

方法区(又叫静态区)特点:

  1. JVM只有做一个方法区,被所有线程共享

  2. 方法区实际也是堆,只是用于存储类、常量相关的信息

  3. *用来存放程序中永远是不变或唯一的内容。(类信息,class对象,静态变量,字符串常量等)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X2WdvRh6-1589329523683)(E:\java ee\笔记\Java SE\images\内存.png)]

构造方法


构造器也叫构造方法(constructor),用于对象的初始化、

要点:

  1. 通过new关键字调用
  2. 构造器虽然有返回值,但是不能定义返回值类型(返回值得类型肯定是本类),不能再构造器里使用return返回某个值
  3. 如果我们没有定义构造器,则编译器会自动定义一个无参的构造函数。如果已定义则编译器不会自动添加。
  4. 构造区的方法名必须和类名一致。

构造方法的重载

构造方法重载和方法的总在要素一样

  • 构造方法的第一句总是super()
  • this表示创建好的对象

垃圾回收机制(Garbage Collection)


内存管理

  • Java的内存管理很大程度值得就是对象的管理,其中包括对象空间的分配和释放。
  • 对象空间的分配:使用new关键字创建对象即可
  • 对象空间的释放:将对象赋值null即可。垃圾回收器将负责回收所有“不可达”对象的内存空间

垃圾回收过程

任何一种垃圾回收算法一般都要做两件基本事情:

  1. 发现无用的对象
  2. 回收无用对象占用的内存空间

垃圾回收机制保证可以将“无用的对象”进行回收。无用的对象指的就是没有任何变量引用对象。Java的垃圾回收器通过其相关算法发现无用对象,并进行清除和整理

垃圾回收相关算法

  1. 引用计数法

    堆中每个对象有一个引用计数。被引用一次,计数加1,被引用变量值为null,则计数减1,值到计数为0,则表示变成无用对象。优点是算法简单,缺点是“循环引用的无用对象”无法识别

    public class Student{
        String name;
        Student friend;
        public static void main(String[] args){
            Student s1 = new Student();
            Student s2 = new Student();
            
            s1.friend = s2;
            s2.friend = s1;
            s1 = null;
            s2 = null;
        }
    }
    

    s1和s2互相引用对方,导致他们引用计数不为0,但是实际已经无用,但无法被识别。

    2.引用可达法(根搜索算法)

    程序把所有的引用关系看做一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当然所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用的节点,即无用节点。

    通用的分代垃圾回收机制

    我们将对象分为三种状态:年轻代、年老代、持久代(存在方法区里)

    JVM将堆内存划分为Eden、Survivor和Tenured/Old空间。

    1. Eden区:存储了从未通过垃圾回收的新对象
    2. Survivor区:存放垃圾回收后,仍然有用的对象,循环存放,小鱼15次垃圾回收次数
    3. Tenured区:年老代区域存放超过15次垃圾回收的对象
  • 新建对象存在在Eden中,当Eden满了,不能创建新对象,则触发垃圾回收(GC),将无用的清理掉,然后剩余对象赋值到Survivor中,以此类推。当old区满了,则会触发一个一次完整的垃圾回收
    • Minor GC:用于清理年青代区域。Eden区满了就会触发一次Minor GC。清理无用对象,将有用对象赋值到“Suvivor1”、“Suvivor2”区中(这两个区,大小空间相同,同一时刻Suvivor1和Suvivor2只有一个在用,一个为空)
    • Major GC:用于清理老年代区
    • Full GC:用于清理年青代、年老代区域。成本较高,会对系统性能产生影响(通长堆垃圾回收机制的优化,是对此进行优化

JVM调优和Full GC

在对JVM调优的过程中,很大一部分工作就是对Full GC的调节。有如下原因可能导致Full GC:

  1. 年老代(Tenured)被写满
  2. 持久代(Perm)被写满
  3. System.gc()被显式调用(程序建议GC启动,不是调用GC 只是建议,并不是调用)
  4. 上一次GC后Heap(堆)的各域分配策略动态变化

开发中容易造成内存泄漏的操作


如下四种情况时最容易造成内存泄漏的场景,开发时一定注意:

  • 创建大量无用对象

    比如,我们在需要大量拼接字符串时,使用了String而不是StringBuilder。

    String str = "";
    for(int i = 0; i < 1000; i++){
        str += i;//相当于产生了1000个String对象
    }
    
  • 静态集合类的使用

    象HashMap、Vector、List等的使用最容易出现内存泄漏,这些静态变量的生命周期和应用程序一致,所有的对象Objec也不能被释放。

  • 各种连接对象(IO流对象、数据库连接对象,网络连接对象)未关闭

    IO流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。

  • 监听器的使用

    释放对象时,没有删除相应的监听器。

要点:
  1. 程序员无权调用垃圾回收器
  2. 程序员可以调用System.gc(),该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full GC,成本高,影响系统性能。
  3. finalize方法,是Java提供给程序员用来释放对象或资源的方法,但是尽量少用。

对象创建的过程和this的本质


  • ##### 创建一个对象分为如下四步:,1. 分配对象空间,并将对象成员变量初始化为0或空,2. 执行属性值的显示初始化,3. 执行构造方法,4. 返回对象的地址给相关变量

this关键字

this的本质就是“创建好的对象的地址”由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象”。

区分成员变量和局部变量

**this调用构造器 this(a,b,c) 且构造器的调用只能在第一句 **

this不能用于static方法中

static关键字

在类中,用static声明的成员变量为静态成员变量,也称为类变量。类变量的生命周期和类相同,在整个引用程序执行期间都有效。

static修饰的成员变量和方法,从属于类。

普通变量和方法从属于对象。

  • 在类中,用static声明的成员变量为静态成员变量,或者叫做:类属性,类变量。

  • 它为该类的公有变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化

  • 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享。

  • 可以使用“对象.类属性”来调用。不过,一般都是用“类名.类属性”

  • static变量置于方法区中

  • 用static声明的方法为静态方法

    • 不需要对象,就可以调用(类名.方法名)
    • 在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非stati的成员。
    • 静态方法不能以任何方式引用this和super关键字

静态初始化块


  • 如果希望加载后,对整个类进行某些初始化操作,可以使用stati初始化块
  • 类第一次被载入时先执行static代码块;类多次被加载时,static代码块只执行一次;static经常用来进行static变量的初始化
  • 是在类初始化时执行,不能再创建对象时执行。
  • 静态初始化块中不能访问非static成员。

参数传值机制

方法中所有参数都是“值传递”,也就是“传递的是值得副本”,不会影响原件

引用类型参数的传值

传递的是值得副本,但是引用类型指的是“对象的地址”。因此,副本和参数都指向了同一个“地址”,改变“副本指向地址的对象的值,也意味着原参数指向对象的值也发生了改变”

Java包(package)概念


包机制是Java中管理类的重要手段。开发中,我们会遇到大量同名的类,通过包我们很容易对解决类重名的问题,也可以实现对类的有效管理。包对于类,相当于文件夹对于文件的作用。

域名倒置

继承的实现(extends)


  1. 父类也称做超类,基类派生类等。
  2. Java中只有单继承,没有c++那样多继承。多继承会引起混乱,是得继承链过于复杂,系统按已维护
  3. Java中类没有多继承,接口有多实现
  4. 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
  5. 如果定义一个类时没有调用extends,则它的父类时:java.lang.Object.

instanceof运算符

instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则返回false

方法的重写override


  1. 方法名、形参列表相同
  2. 返回值类型和声明异常类型,子类小于等于父类
  3. 访问权限,子类大于等于父类

Object类


Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法

toString()方法

将HashCode转换为16进制输出

==和equals方法

  • “==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。比较对象是否相同
  • equals 比较内容是否相同
    • Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。
    • 比较两个对象的hashcode值

super


super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。

继承树追溯

构造方法调用顺序;

**构造方法第一句总是super(…)**来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复

封装的作用和含义


“高内聚,低耦合”

高内聚就是类的内部数据操作细节自己完成,不允许外不干涉

低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用

封装的优点:

  1. 提高代码安全性
  2. 提高代码复用性
  3. “高内聚”:封装细节,便于修改内部代码,提高可维护性。
  4. “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。

封装的实现——使用访问控制符

修饰符 同一个类 同一个包中 不同包子类 所有类
private *
default * *
protected * * *
public * * * *
  1. private 表示私有的的,只有自己类能访问
  2. default 表示没有修饰符,只有同一个包的类能访问
  3. protected 表示可以被同一个包的类以及其他包中的子类访问
  4. public 表示可以被该项目的所有包中的所有类访问

封装的使用细节

类的属性的处理:
  1. 一般使用private访问权限
  2. 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头)
  3. 一些只用于本类的辅助性方法可以用private修饰符,希望其他类调用的方法用public修饰。

多态(polymorphism)


多态指的是同一个方法调用,由于对象不同可能会有完全不同的行为。现实生活中,同一个方法,具体实现会完全不同。

多态的要点:

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)
  2. 多态的存在要有3个必要条件:继承、方法重写、父类引用指向子类对象
  3. 父类引用指向子类对象后,用该类引用调用子类重写的方法,此时多态就出现了。

对象的转型(casting)


父类引用指向子类对象对象,我们称这个过程为向上转型,属于自动类型转换。

向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型。

final关键字


  1. 修饰变量:被修饰的变量不可改变,一旦赋了初值,就不能被重新赋值
  2. 修饰方法:该方法不可被子类重写。但是可以被重载
  3. 修饰类:修饰的类不能被继承

数组概述和特点


数组是相同类型数据的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。

数组的三个基本特点:

  1. 长度是确定的。数组一旦被创建,它的大小就是不可以改变的
  2. 其元素必须是相同类型,不允许出现混合类型
  3. 数组类型可以是任何数据类型,包括基本类型和引用类型

数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当与该对象的成员变量。

注意事项:
  1. 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。
  2. 声明一个数组的时候并没有数组真正被创建
  3. 构造一个数组,必须制定长度。

抽象方法和抽象类(abstract)


抽象类的使用要点:

  1. 有抽象方法的类只能定义成抽象类
  2. 抽象类不能实例化,即不能用new来实例化抽象类
  3. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用
  4. 抽象类只能用来被继承
  5. 抽象方法必须被子类实现

接口的作用


定义接口的详细说明:

  1. 访问修饰符:只能是public或默认
  2. 接口名:和类名采用相同的命名机制
  3. extends:接口可以多继承
  4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是
  5. 方法:接口中的方法只能是public abstract。省略的话 也是。

要点:

  1. 子类通过implements来实现接口中的规范
  2. 接口不能创建实例,但是可用于声明引用变量类型
  3. 一个类实现了接口,必须实现接口中所有的方法,并且这些方法之只能是public的。
  4. JDK1.7之前,接口中只能包含静态常量、抽象方法、不能有普通属性、构造方法、普通方法
  5. JDK1.8后,接口中包含普通静态方法

内部类的分类


在Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类

非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)

  • 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
  • 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员
  • 非静态内部类不能有静态方法、静态属性和静态初始化块
  • 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例
成员变量访问要点:
  1. 内部类里方法的局部变量:变量名
  2. 内部类属性:this.变量名
  3. 外部类属性:外部类名.this.变量名
package com.junwei.oop;

public class TestInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //创建内部类对象
        Outer.Inner inner = new Outer().new Inner();
        inner.show();
    }
}
class Outer{
    private int age = 10;
    public void testOuter(){

    }
    /**内部类*/
    class Inner{
        int age = 30;
        public void show(){
            int age = 20;
            System.out.println("外部类的成员变量age:" + Outer.this.age);
            System.out.println("方法内变量age:" + age);
            System.out.println("内部类的成员变量age:" + this.age);
        }
    }
}

静态内部类

package com.junwei.oop;

public class TestInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //创建内部类对象
        Outer.Inner inner = new Outer().new Inner();
        inner.show();
    }
}
class Outer{
    private int age = 10;
    public void testOuter(){

    }
    /**内部类*/
    class Inner{
        int age = 30;
        public void show(){
            int age = 20;
            System.out.println("外部类的成员变量age:" + Outer.this.age);
            System.out.println("方法内变量age:" + age);
            System.out.println("内部类的成员变量age:" + this.age);
        }
    }
}

匿名内部类

package com.junwei.oop;

/**
 * 匿名内部类
 */
public class TestAnonymousInnerClass {
    public static void main(String[] args) {
        TestAnonymousInnerClass.test(new AA() {
            @Override
            public void aa() {
                System.out.println("你好匿名内部类!");
            }
        });
        TestAnonymousInnerClass.test(()-> System.out.println("Lambda表达式实现匿名内部类!"));
    }
    public static void test(AA a){
        a.aa();
    }
}
interface AA{
    void aa();
}

String基础


String类和常量池

在java的内存分析中,我们会经常听到关于“常量池”的描述,实际上常量池也分了以下三种:

全局字符串常量池(String Pool)

全局字符串常量池中存放的内容实在类加载完成后存到String pool中的,在每个JVM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)

class文件常量池(Class Constant Pool)

class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用

运行时常量池(Runtime Constant Pool)

运行时常量池在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用保持一致。

通长比较字符串使用equals

String类常用方法

package com.junwei.oop;

public class TestString {
    public static void main(String[] args) {
        String s1 = " core Java ";
        String s2 = "Core Java";
        //提取下标为3的字符 从0开始
        System.out.println(s1.charAt(3));
        //字符串的长度 算上空格
        System.out.println(s2.length());
        //比较两个字符串是否相等
        System.out.println(s1.equals(s2));
        //比较两个字符串(忽略大小写)
        System.out.println(s1.equalsIgnoreCase(s2));
        //字符串s1中是否包含Java 返回首字符位置
        System.out.println(s1.indexOf("Java"));

        //将s1中的空格替换成&
        String s = s1.replace(' ','&');
        System.out.println(s);
        //是否以core开头
        System.out.println(s1.startsWith("core"));
        //是否以Java结尾
        System.out.println(s1.endsWith("Java"));
        //提取字符串:从下标为4的开始到字符串结尾为止
        System.out.println(s1.substring(4));
        //提取字符串:下标[4,7)不包括7 前包后不包
        System.out.println(s1.substring(4,7));
        //转小写
        System.out.println(s1.toLowerCase());
        //转大写
        System.out.println(s1.toUpperCase());
        //去除首位空格
        System.out.println(s1.trim());
    }
}

数组的拷贝


arrayCopy

package com.junwei.array;

import org.jetbrains.annotations.Contract;
import org.junit.Test;

import java.util.Scanner;

public class TestArrayCopy {
    static String[] s1 = {"aa","bb","cc","dd","ee"};
    public static void main(String[] args) {
        String[] s2 = new String[10];
        //arrayCopy数组拷贝方法
        // 要拷贝的数组,从哪开始拷贝,拷贝到哪个数组中,从新数组中的哪个位置开始,要拷贝的长度是多少
        System.arraycopy(s1,2,s2,3,3);
        for (String str : s2
             ) {
            System.out.print(str+"\t");
        }
        System.out.println();
        testDel();
        System.out.println("----------------------");
        while (true) {
            testInsert();
            System.out.println();
            System.out.println("++++++++++++++++++++++++");
        }
    }

    /**删除字符串指定元素的方法*/
    public static String[] deleteElement(String[] str,int index){
        System.arraycopy(str,index+1,str,index,str.length-index-1);
        str[str.length-1] = null;
        return str;
    }
    /**在数组指定位置插入元素*/
    public static String[] insertElement(String[] strs, int index, String str){
        //如果原数组已满 进行扩容
        if (isFull(strs)){
            String[] strings = new String[strs.length+strs.length];
            System.arraycopy(strs,0,strings,0,strs.length);
            System.arraycopy(strings,index,strings,index+1,strings.length-index-1);
            strings[index] = str;
            return strings;
        }else {
            System.arraycopy(strs,index,strs,index+1,strs.length-index-1);
            strs[index] = str;
            return strs;
        }
    }

    /**判断数组是否以满*/
    @Contract(pure = true)
    public static boolean isFull(String[] strs){
       for (int i = 0; i < strs.length; i++){
           if (strs[i] == null){
               return false;
           }
       }
        return true;
    }
    @Test
    public void test3(){
        String str = null;
        System.out.println(str == null);
    }

    public static void testInsert(){
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要插入的元素:");
        String str = sc.next();
        System.out.println("请输入要插入元素的位置:");
        int index = sc.nextInt();
        String[] strings = insertElement(s1, index, str);
        if (strings.length!=s1.length){
            s1 = strings;
        }
        for(String s : strings){
            System.out.print(s+"\t");
        }
    }
    public static void testDel(){
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要删除的元素下标");
        int index = sc.nextInt();
        String[] strings = deleteElement(s1, index);
        for(String str : strings){
            System.out.print(str+"\t");
        }
    }

    @Test
    public void test(){
        //需求:将s1字符串cc删除
        System.arraycopy(s1,3,s1,2,2);
        s1[s1.length-1] = null;
        for(String str : s1){
            System.out.print(str+"\t");
        }
    }
    /**数组的扩容
     * 本质上是:先定义一个更大的数组,然后将原数组内容原封不动的拷贝到新数组中
     * */
    public static void extendRange(){
        String[] s1 = {"aa","bb"};

        String[] s2 = new String[s1.length+10];
        System.arraycopy(s1,0,s2,0,s1.length);
    }
}

Arrays工具类的使用

包含了排序、查找、填充、打印内容等常见操作

package com.junwei.array;

import java.util.Arrays;

public class TestArrays {
    public static void main(String[] args) {
        int[] nums = {2,4,1,3,8,4,5,23,12,31};
        ///输出
        System.out.println(Arrays.toString(nums));
        //排序
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));
        //查找 返回索引位置 如果查不到 返回 -1
        System.out.println(Arrays.binarySearch(nums,3));
    }
}

冒泡排序的基础算法

package com.junwei.array;

import java.util.Arrays;
/***冒泡排序及优化*/
public class TestBubbleSort {
    public static void main(String[] args) {
        int[] nums = {2,4,1,3,8,4,5,23,12,31};
        int temp = 0;
        for (int j = 0; j < nums.length-1; j++) {
            //定义标志位
            boolean flag = true;
            for (int i = 0; i < nums.length-1-j; i++) {
                //比较大小,换顺序
                if (nums[i]>nums[i+1]){
                    temp = nums[i];
                    nums[i] = nums[i+1];
                    nums[i+1] = temp;
                    flag = false;
                }
                System.out.println(Arrays.toString(nums));
            }
            if (flag){
                System.out.println("结束");
                break;
            }
            System.out.println("---------------");
        }
    }
}

二分法查找(折半查找)

package com.junwei.array;

import java.util.Arrays;

/**
 * 二分法查找
 */
public class TestBinarySearch {
    public static void main(String[] args) {
        int[] nums = {2,4,1,3,8,4,5,23,12,31};
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));
        int i = myBinarySearch(nums, 12);
        System.out.println("位置:" + i);
    }
    public static int myBinarySearch(int[] nums, int value){
        int low = 0;
        int high = nums.length;

        while (low <= high){
            int mid = (low + high)/2;

            if (value == nums[mid]){
                return mid;
            }
            if (value > nums[mid]){
                low = mid + 1;
            }
            if (value < nums[mid]){
                high = mid - 1;
            }
        }
        return -1;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章