1. Java语言特性
- 简单
Java语法是C++语法的一个“纯净版本”,相当于对C++做了一个减法。这里没有头文件、指针运算(甚至指针 语法)、结构、联合、操作符重载、虚基类等等。不仅如此,Java开发环境远远超出大多数其他编程语言的开 发环境。
- 面相对象
在Java的世界里,一切皆对象。比如:人、狗、手机、电脑等都是对象。所谓面相对象,就是依靠对象之间 的交互来完成事情。
- 分布式(微服务)
Java有丰富的例程库,用于处理像HTTP和FTP之类的TCP/IP协议。Java应用程序能够通过URL打开和访问网络
上的对象,其便捷程度就好像访问本地文件一样。
- 健壮性
Java采用的指针模型可以消除重写内存和损坏数据的可能性,Java编译器能够 检测许多在其他语言中仅在运行时才能够检测出来的问题。
- 安全性
Java适用于网络/分布式环境。为了达到这个目标,在安全性方面投入了大量的精力。使用Java可以构建防病
毒、防篡改的系统
- 体系结构中立
编译器生成一个体系结构中立的目标文件格式,按照该中规范生成的文件,只要有Java运行时系统,这些编译后的代码就可以在许多处理器上运行。Java编译器通过生成与特定计算机体系结构无关的字节码指令来实现 这一特性。字节码不仅可以很容易的在任何机器上解释执行,而且还可以动态地翻译成本地机器代码。“Wrice once,Run anywhere”。其他语言编写的程序,在编译后如果能够严格按照字节码文件的规范生成.class文件,也可以在JVM上运行。
7.可移植性
在Java中,数据类型具有固定的大小,这消除了代码移植时的主要问题。
- 解释性
Java为了实现与平台无关,自己维护了一套基于栈架构的指令集,Java源代码经过编译之后,字节码文件中的指令就是按照自己的指令集来组织的,但是在具体硬件环境中运行时,系统并不能识别,因为Java程序在执行 时,Java解释器会逐条的将字节码文件中的指令翻译成CPU的指令集。
- 高性能
边解释边执行,垃圾回收等导致了Java代码运行效率偏低,近年来JVM也在不断的优化,比如:JIT(即时编译器),热点代码探测,让Java程序的执行效率大幅提高。
- 多线程
Java是第一个支持并发程序设计的主流语言,多线程可以带来更好的交互响应和实时行为。
- 动态性
它能够适应不断发展的环境,库中可以自由的添加新方法和实例变量,而对客户端没有任何影响。在Java中找出运行时类型信息十分简单。Java不仅仅是一门编程语言,也是一个由一些列计算机软件和规范组成的技术体系。
2. Java程序的运行
Java是一门半编译型、半解释型语言。先通过javac编译程序把源文件进行编译,编译后生成的.class文件是由字节 码组成的平台无关、面向JVM的文件。最后启动java虚拟机来运行.class文件,此时JVM会将字节码转换成平台能够 理解的形式来运行。
在运行Java程序前,必须先安装好JDK(Java Development Kit即Java开发工具包),JDK里面就包含了javac和 java工具,Java程序最终是在JVM(Java虚拟机)中运行的。
JDK、JRE、JVM之间的关系:
- JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。
- JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程 序运行的所需环境。
- JVM:Java虚拟机,运行Java代码
3. 关键字
- 用于定义访问权限修饰符:private, protected, public,default
- 用于定义类,函数,变量修饰符:abstract, final, static, synchronized
- 用于定义类与类之间的关系:extends,implements
- 用于定义建立实例及引用实例,判断实例:new,this,super,instanceof
- 用于异常处理:try,catch,finally,throw,throws
- 用于包:package,import
- 其他修饰符关键字:native,strictfp,transient,volatile,assert
4. 注释
- 单行注释:// 注释内容(用的最多)
- 多行注释:/* 注释内容*/(不推荐)
- 文档注释: /** 文档注释 */(常见于方法和类之上描述方法和类的作用),可以被javadoc工具解析,生 成一套以网页文件形式体现的程序说明文档
- 多行注释不能嵌套使用
- 不论是单行还是多行注释,都不参与编译,即编译之后生成的.class文件中不包含注释信息。
5. 标识符
在程序中由用户给类名、方法名或 者变量所取的名字。
由数字,字母,下划线,美元符号组成,
不能以数字、关键字开头,且严格区分大小写。
建议 :
- 类名 :每个首字母大写(大驼峰)
- 方法名:首字母小写,后面每个字母的首字母大写(小驼峰)
- 变量名:首字母小写,后面每个字母的首字母大写(小驼峰)
6. 字面常量
常量即程序运行期间,固定不变的量称为常量
示例:
System.Out.println("Hello World");
不论程序何时运行,输出的都是Hello World,其实"Hello World"就是字面常量。
6.1 字面常量的分类
- 字符串常量:由""括起来的,比如“12345”、“hello”、“你好”。
- 整形常量:程序中直接写的数字(注意没有小数点),比如:100、1000 。
- 浮点数常量:程序中直接写的小数,比如:3.14、0.49。
- 字符常量:由 单引号 括起来的当个字符,比如:‘A’、‘1’。
- 布尔常量:只有两种true和false。
- 空常量:null。
7. 数据类型
7.1 四类基本数据类型
整型,浮点型,字符型,布尔型
7.1.1 八种
byte,short,int,long,float,double,char,boolean。
- 不论是在16位系统还是32位系统,int都占用4个字节,long都占8个字节
- 整形和浮点型都是带有符号的
- 整型默认为int型,浮点型默认为double
字节是计算机中表示空间大小的基本单位.
计算机使用二进制表示数据. 我们认为 8 个二进制位(bit) 为一个字节(Byte). 我们平时的计算机为 8GB 内存, 意思是 8G 个字节.其中 1KB = 1024 Byte, 1MB = 1024 KB, 1GB = 1024 MB.所以 8GB 相当于 80 多亿个字节.
7.2 引用数据类型String
7.2.1 String类的构造方式:
使用常量串构造:
直接newString对象:
使用字符数组进行构造:
String是引用类型,内部并不存储字符串本身,在JDK1.8中,字符串实际保存在char类型的数组中。在Java中“”引起来的也是String类型对象。
7.2.1.1 字符串常量池:
为了节省存储空间以及程序的运行效率,Java中引入了:
- Class文件常量池:
每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息。- 运行时常量池:
在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份。- 字符串常量池:
字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable在堆中 ,大小可设置,有范围限制,最小是1009。
7.2.1.2 String对象创建的区别:
- 直接使用字符串常量进行赋值:
在字节码文件加载时,常量串已经加载好了并保存在字符串常量池中,创建对象时,先在字符串常量池中找,将字符串引用赋值给对象。使用常量串创建String类型对象的效率更高,而且更节省空间,也可以将创建的字符串对象通过 intern 方式添加进字符串常量池中。 - 通过new创建String类对象:
只要是new的对象,都是唯一的。
- String str = “hello”:
只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象。- String str = new String(“hello”):
会开辟两块堆内存空间,字符串"hello"保存在字符串常量池中,然后用常量池中的String对象给新开辟的String对象赋值。- String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’}):
先在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到String对象中。
7.2.1.3 字符串的不可变性:
String是一种不可变对象. 字符串中的内容是不可改变。
String类中的字符实际保存在内部维护的value字符数组中,而且String类被final修饰表明该类不能被继承,value被修饰被final修饰表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象。
7.2.1.3.1 为什么 String 要设计成不可变的?
- 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
- 不可变对象是线程安全的.
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中
7.2.2 String对象的比较:
-
==
比较是否引用同一个对象(对于内置类型,== 比较的是变量中的值;对于引用类型 == 比较的是引用中的地址)。 -
boolean equals(Object anObject) 方法
按照字典序(字符大小的顺序)比较。先检测this 和 anObject 是否为同一个对象比较,如果是返回true,检测anObject是否为String类型的对象,如果是继续比较,否则返回false,将anObject向下转型为String类型对象,this和anObject两个字符串的长度是否相同,是继续比较,否则返回false,按照字典序,从前往后逐个字符进行比较。 -
int compareTo(String s) 方法
按照字典序进行比较:1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值,2. 如果前k个字符相等(k为两个字符长度最小值),返回两个字符串长度差值。 -
int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较。
7.2.3 字符串查找:
- char charAt(int index):返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常。
- int indexOf(int ch) :返回ch第一次出现的位置,没有返回-1。
int indexOf(int ch, int fromIndex):从fromIndex位置开始找ch第一次出现的位置,没有返回-1。 - int indexOf(String str):返回str第一次出现的位置,没有返回-1。
- int indexOf(String str, intfromIndex):从fromIndex位置开始找str第一次出现的位置,没有返回-1。
- int lastIndexOf(int ch):从后往前找,返回ch第一次出现的位置,没有返回-1。
- int lastIndexOf(int ch, int fromIndex):从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1。
int lastIndexOf(String str) :从后往前找,返回str第一次出现的位置,没有返回-1。 - int lastIndexOf(String str, int fromIndex):从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1。
7.2.4 转化:
- 数值和字符串转化:Integer.parseInt()Double.parseDouble()……
- 大小写转换:toUpperCase(),toLowerCase()。
- 字符串转数组:toCharArray(),
- 数组转字符串:new String(ch)
- 格式化:format(“%d-%d-%d”, 2019, 9,14)
7.2.5 替换:
- 替换所有的指定内容:String replaceAll(String regex, String replacement)
- 替换首个内容:String replaceFirst(String regex, String replacement)
由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串。
7.2.6 字符串拆分:
- 将字符串全部拆分:String[] split(String regex)
- 将字符串以指定的格式,拆分为limit组:String[] split(String regex, int limit)
字符"|“,”*“,”+“都得加上转义字符,前面加上 “\” .
而如果是 “” ,那么就得写成 “\\” .
如果一个字符串中有多个分隔符,可以用”|"作为连字符.
7.2.7 字符串截取:
- 从指定索引截取到结尾:String substring(int beginIndex)
- 截取部分内容:String substring(int beginIndex, int endIndex) 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
7.2.8 字符串修改:
尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下,如果要修改建议尽量使用StringBuffer或者StringBuilder。
7.2.8.1 String和StringBuilder类转换。
- String变为StringBuilder: 利用StringBuilder的构造方法或append()方法。
- StringBuilder变为String: 调用toString()方法。
7.2.8.2 String、StringBuffer、StringBuilder的区别:
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改。
- StringBuffer与StringBuilder大部分功能是相似的。
- StringBuffer采用同步处理,属于线程安全操作;而- StringBuilder未采用同步处理,属于线程不安全操作。
7.2.9 其他方法
- 去掉字符串中的左右空格,保留中间空格:String trim() ,trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)
- 字符串转大写:String toUpperCase()
- 字符串转小写:String toLowerCase()
8. 枚举类型
在Java当中枚举实际上就是一个类,将常量组织起来统一进行管理,是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类。枚举的构造方法默认是私有的。枚举常量更简单安全 。 枚举具有内置方法 ,代码更优雅,但是不可继承,无法扩展。应用场景:错误状态码,消息类型,颜色的划分,状态机等等…
Enum 类的常用方法:
以数组形式返回枚举类型的所有成员values() ;获取枚举成员的索引位置ordinal() ;将普通字符串转换为枚举实例valueOf() ;比较两个枚举成员在定义时的顺序compareTo()。
9. 变量
有些内容可能会经常改变,在Java程序中,称为变量,数据类型就是用来定义不同种类变量的。
9.1 语法格式
数据类型 变量名 = 初始值;
示例:
int a = 10; // 定义整形变量a,a是变量名也称为标识符,该变量中放置的值为10
double d = 3.14;
char c = 'A';
boolean b = true;
9.2 整型(int)变量
- int不论在何种系统下都是4个字节
- 如果没有合适的初始值,可以设置为0
- 在给变量设置初始值时,值不能超过int的表示范围,否则会导致溢出
- 变量在使用之前必须要赋初值,否则编译报错
- int的包装类型为 Integer
9.3 长整型(long)变量
- 长整型变量的初始值后加L或者l,推荐加L
- 长整型不论在那个系统下都占8个字节
- 长整型的表示范围为: ~
- long的包装类型为Long
9.4 短整型(short)变量
- short在任何系统下都占2个字节
- short的表示范围为:-32768 ~ 32767
- 使用时注意不要超过范围(一般使用比较少)
- short的包装类型为Short
9.5 字节型(byte)变量
- byte在任何系统下都占1个字节
- byte的范围是:-128 ~ 127
- 字节的包装类型为Byte
9.6 双精度浮点型(double)变量
- double在任何系统下都占8个字节
- 浮点数与整数在内存中的存储方式不同,不能单纯使用 的形式来计算
- double的包装类型为Double
- double 类型的内存布局遵守 IEEE 754 标准(和C语言一样), 尝试使用有限的内存空间表示可能无限的小数, 势
必会存在一定的精度误差,因此浮点数是个近似值,并不是精确值。 - 在 Java 中, int 除以 int 的值仍然是 int(会直接舍弃小数部分)。如果想得到 0.5, 需要使用 double 类型计算.
9.7 单精度浮点型(float)变量
float 类型在 Java 中占四个字节, 同样遵守 IEEE 754 标准. 由于表示的数据精度范围较小, 一般在工程上用到浮点数 都优先考虑 double, 不太推荐使用 float. float的包装类型为Float。
9.8 字符型(char)变量
- Java 中使用 单引号 + 单个字母 的形式表示字符字面值.
- 计算机中的字符本质上是一个整数. 在 C 语言中使用 ASCII 表示字符, 而 Java 中使用 Unicode 表示字符. 因此 一个字符占用两个字节, 表示的字符种类更多, 包括中文.
- char的包装类型为Character
9.9 布尔型(boolean)变量
- boolean 类型的变量只有两种取值, true 表示真, false 表示假.
- Java 的 boolean 类型和 int 不能相互转换, 不存在 1 表示 true, 0 表示 false 这样的用法.
9.10 类型转换
Java 是一个强类型编程语言, 当不同类型之间的变量相互赋值的时候, 会有教严格的校验.
- 不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型
- 如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢失
- 将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查
- 强制类型转换不一定能成功,不相干的类型不能互相转换
9.10.1 自动类型转换(隐式)
代码不需要经过任何处理,在代码编译时,编译器会自动进行处理。数据范围小的转为数 据范围大的时会自动进行。
9.10.1 强制类型转换(显式)
当进行操作时,代码需要经过一定的格式处理,不能自动完成。
特点:数据范围大的到数据范围小的。
9.11 类型提升
不同类型的数据之间相互运算时,数据类型小的会被提升到数据类型大的。
- int与long之间:int会被提升为long
- byte与byte的运算:
byte 和 byte 都是相同类型, 但是出现编译报错. 原因是, 虽然 a 和 b 都是 byte, 但是计算 a + b 会先将 a 和 b 都提升成 int, 再进行计算, 得到的结果也是 int, 这是赋给 c, 就会出现上述错误.由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 诸如 byte 和 short 这种低于 4 个字节的类型, 会先提升成 int, 再参与计算。byte a = 10; byte b = 20; byte c = a + b; System.out.println(c); // 编译报错 Test.java:5: 错误: 不兼容的类型: 从int转换到byte可能会有损失
正确的写法:byte a = 10; byte b = 20; byte c = (byte)(a + b); System.out.println(c);
10. 数组
- 数组数组是引用类型,可以看成是相同类型元素的一个集合,在内存中是一段连续的空间。
- 数组中存放的元素其类型相同
- 数组的空间是连在一起的
- 每个空间有自己的编号,其实位置的编号为0,即数组的下标。
10.1 数组的创建格式
T[] 数组名 = new T[N];
10.2 数组的初始化
如果没有对数组进行初始化,数组中元素有其默认值,如果数组中存储元素类型为引用类型,默认值为null, 如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值
- 动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array = new int[10];
- 静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
T[] 数组名称 = {data1, data2, data3, ..., datan} int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9}; double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0}; String[] array3 = new String[]{"hell", "Java", "!!!"};
静态初始化时, {}中数据类型必须与[]前数据类型一致。
静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
静态初始化可以简写,省去后面的new T[]。
静态和动态初始化也可以分为两步,但是省略格式不可以。int[] array1; array1 = new int[10]; int[] array2; array2 = new int[]{10, 20, 30}; // 注意省略格式不可以拆分, 否则编译失败 // int[] array3; // array3 = {1, 2, 3};
10.3 数组的使用
10.3.1 数组中元素访问
数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,支持随机访问,数组可以通过下标访问其任意位置的元素。下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
10.3.2 遍历数组
“遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作。
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
10.4 数组的存储
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值; 而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。
示例:
public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
在上述代码中,a、b、arr,都是函数内部的变量,因此其空间都在main方法对应的栈帧中分配。 a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。 array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
10.4.1 数组的修改
public static void func() {
int[] array1 = new int[3];
array1[0] = 10;
array1[1] = 20;
array1[2] = 30;
int[] array2 = new int[]{1,2,3,4,5};
array2[0] = 100;
array2[1] = 200;
array1 = array2;
array1[2] = 300;
array1[3] = 400;
array2[4] = 500;
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
10.4.2 数组拷贝
- 借助copyOf() 方法
import java.util.Arrays;
public static void func(){
// newArr和arr引用的是同一个数组
// 因此newArr修改空间中内容之后,arr也可以看到修改的结果
int[] arr = {1,2,3,4,5,6};
int[] newArr = arr;
newArr[0] = 10;
System.out.println("newArr: " + Arrays.toString(arr));
// 使用Arrays中copyOf方法完成数组的拷贝:
// copyOf方法在进行数组拷贝时,创建了一个新的数组
// arr和newArr引用的不是同一个数组
arr[0] = 1;
newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
// 因为arr修改其引用数组中内容时,对newArr没有任何影响
arr[0] = 10;
System.out.println("arr: " + Arrays.toString(arr));
System.out.println("newArr: " + Arrays.toString(newArr));
// 拷贝某个范围.
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
}
- 实现copyOf()
public static int[] copyOf(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i];
}
return ret;
}
10.4 二维数组
- 基本语法
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
使用示例:
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
// 执行结果
1 2 3 4
5 6 7 8
9 10 11 12
10.5 数组练习
10.5.1 求数组中元素的平均值
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(avg(arr));
}
public static double avg(int[] arr) {
int sum = 0;
for (int x : arr) {
sum += x;
}
return (double)sum / (double)arr.length;
}
// 执行结果
3.5
10.5.2 查找数组中指定元素(顺序查找)
public static void main(String[] args) {
int[] arr = {1,2,3,10,5,6};
System.out.println(find(arr, 10));
}
public static int find(int[] arr, int data) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == data) {
return i;
}
}
return -1; // 表示没有找到
}
// 执行结果
3
10.5.3 查找数组中指定元素(二分查找)
针对有序数组, 可以使用更高效的二分查找.
以升序数组为例, 二分查找的思路是先取中间位置的元素, 然后使用待查找元素与数组中间元素进行比较: 如果相等,即找到了返回该元素在数组中的下标
如果小于,以类似方式到数组左半侧查找 如果大于,以类似方式到数组右半侧查找
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (toFind > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
return mid;
}
}
// 循环结束, 说明没找到
return -1;
}
// 执行结果
5
10.5.4 冒泡排序
假设排升序:
- 将数组中相邻元素从前往后依次进行比较,如果前一个元素比后一个元素大,则交换,一趟下来后最大元素 就在数组的末尾
- 依次从上上述过程,直到数组中所有的元素都排列好
public static void main(String[] args) {
int[] arr = {9, 5, 2, 7};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j < arr.length-i; j++) {
if (arr[j-1] > arr[j]) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
} // end for
} // end bubbleSort
// 执行结果
[2, 5, 7, 9]
Java 中内置了更高效的排序算法:
public static void main(String[] args) { int[] arr = {9, 5, 2, 7}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); }
10.5.5 数组逆序
设定两个下标, 分别指向第一个元素和最后一个元素. 交换两个位置的元素.
然后让前一个下标自增, 后一个下标自减, 循环继续即可.
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
reverse(arr);
System.out.println(Arrays.toString(arr));
}
public static void reverse(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
11. 运算符
-
算术运算符:±*/
-
增量运算符:+= -= *= %=
-
自增自减运算符:++ –
-
关系运算符:== ,!=,<,>,<=,>=
-
逻辑运算符:&&,||,!
-
位运算符:
- &(如果两个二进制位都是 1, 则结果为 1, 否则结果为 0)
- |(如果两个二进制位都是 0, 则结果为 0, 否则结果为 1)
- ^ (如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1)
- ~(如果该位为 0 则转为 1, 如果该位为 1 则转为 0)
-
移位运算符:
<<(最左侧位不要了, 最右侧补 0) >>(最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)) >>>(最右侧位不要了, 最左侧补 0)
-
条件运算符:表达式1 ? 表达式2 : 表达式3
13. 逻辑控制
顺序结构:按照代码书写的顺序一行一行执行。
分支结构:if,switch(只能是以下类型:byte、char、short、int,String常量串、枚举类型)
循环结构:
while(break(让循环提前结束),continue(跳过这次循环, 立即进入下次循环)),for,do while