Java核心技术
Java 语言的语法与 C 语言和 C++ 语言很接近,Java 丢弃了 C++ 中很少使用的、很难理解的那些特性,如操作符重载、多继承、自动的强制类型转换。特别地,Java 语言不使用指针,而是引用。并提供了自动分配和回收内存空间,不必为内存管理而担忧。
JDK:Java Development Kit(开发者工具)
JRE:Java Runtime Environment(运行时环境)
JVM:Java Virtual Machine(虚拟机)
数值
关键字
在Java中定义的标识符不能与关键字同名。
标识符以字母、数字、下划线、美元符号组成,数字不能开头。
关键字 | ||||
---|---|---|---|---|
abstract | assert | boolean | break | byte |
case | catch | char | class | const |
continue | default | do | double | else |
enum | extends | final | finally | float |
for | goto | if | implements | import |
instanceof | int | interface | long | native |
new | package | private | protected | public |
return | strictfp | short | static | super |
switch | synchronized | this | throw | throws |
transient | try | void | volatile | while |
数据类型
Java中有8大基本数据类型和String类型。
最好避免用浮点类型进行比较。
基本数据类型 | 字节空间 |
---|---|
long | 8 |
double | 8 |
int | 4 |
float | 4 |
char | 2 |
short | 2 |
byte | 1 |
boolean | 1 |
类型转换
Java中数据类型的精度从低到高为:byte,short,char,int,long,float,double
高精度到低精度需要强制转换,丢失部分精度。语法:(类型)(变量名)
低精度到高精度数据类型自动转换。
不能对布尔值进行转换。
常量
常量(Constant):初始化后不能再改变值,不会被改变
关键字:final
运算符
位运算符:在二进制上进行运算
三元运算符:min=x>y?y:x
.equals():比较内容的方法
==:比较内存地址值
比较String类型需要.equals()方法,一般常量放在左边。
public void operator() {
String s1 = "123456";
String s2 = new String("123456");
System.out.println(s1.equals(s2));//true
System.out.println(s1==s2);//false
}
值传递
值传递:对基本数据类型变量,传递的是该变量的一个副本,改变副本不影响原变量。
流程控制
JAVA的基本结构就是顺序结构。
语句与语句之间,框与框之间是按从上到下的顺序进行。
选择
if else
if(bool){
//如果bool表达式为true将执行的语句
}else if(bool){
//如果bool表达式为true将执行的语句
}else{
//如果bool表达式的值为false将执行的语句
}
switch case
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
switch(value){
case value:
//代码块
break;
case value:
//代码块
break;
default:
//代码块
}
循环
只要bool表达式为true,循环就一直执行下去。
循环条件一直为ture就会造成无限循环,少部分情况需要循环一直执行,如服务器的请求响应监听等。
while
循环执行次数是不确定的。
while(布尔表达式){
//代码块
}
public class Test {
public static void main(String[] args) {
int i = 0;
int sum = 0;
while(i<=100){
sum +=i;
i++;
}
System.out.println(sum);
}
}
do...while
先执行后判断,循环至少会执行一次。
do{
//代码语句
}while(bool)
for
最有效、最灵活的循环结构,循环执行次数是确定的。
idea快捷键:100.for
for(初始化;bool表达式;更新){
//代码块
}
增强for循环
声明语句:声明新的局部变量,该变量的类型必须与数组元素的类型匹配,其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法
for(声明语句:表达式){
//代码块
}
public class Test {
int[] numbers = {10,20,30,40,50};
//遍历数组的元素
for (int x:numbers){
System.out.println(x);
}
}
break|continue
break用于强行退出本次循环,不执行循环中剩余的语句。
continue用于终止某次循环,接着进行下一次是否执行循环的判定。
数组
数组:数据容器,相同类型数据的有序集合。
数组变量属引用类型,数组本身就是对象,数组中的每个元素相当于该对象的成员变量。因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
数组元素通过索引访问,索引从0开始。
下标的合法区间:[0,length-1],如果越界就会报错。
ArraylndexOutOfBoundsException:数组下标越界异常!
获取数组的长度:arrays.length
public void test1(){
String[] arr1 = {"张三","李四","王五"};
double[] arr2 = {11,22,33};
int[] arr3 = new int[5];
for (int i=0;i<arr3.length;i++){
int j = (int)(Math.random()*100);
arr3[i] = j;
}
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
System.out.println(Arrays.toString(arr3));
}
多维数组
多维数组可以看成是数组的数组,二维数组就是一个特殊的一堆数组,每一个元素都是一个一维数组。
int arr[] = {1,2,3}
arr[1] = {"a","b","c"};
Arrays类
数组的工具类 java.util.Arrays
由于数组对象本身并没有什么方法可以调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作.
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用。
数组排序
public void ArraySort(){
//要生成多少个随机数
int num = 10;
int[] array = new int[num];
//生成随机数组
public void ProduceNum(){
for(int i=0;i<num;i++){
int j = (int)(Math.random()*100);
array[i] = j;
}
}
//冒泡排序
public void BubbleSort(){
TestArray testArray = new TestArray();
testArray.ProduceNum();
int []arr = testArray.array;
for (int i=0;i<arr.length-1;i++){
for (int j=0;j<arr.length-1-i;j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
//选择排序
public void SelectSort(){
TestArray testArray = new TestArray();
testArray.ProduceNum();
int[] arr = testArray.array;
for (int i=0;i<arr.length-1;i++){
for (int j=i+1;j<arr.length;j++){
if (arr[i]>arr[j]){
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
for (int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
//Arrays.sort方法排序
public void sort() {
TestArray testArray = new TestArray();
testArray.ProduceNum();
int[] arr = testArray.array;
Arrays.sort(testArray.array);
for (int i = 0; i< testArray.num; i++){
System.out.print(testArray.array[i]+" ");
}
System.out.println();
}
public void three() {
//三目运算符排序
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数字");
int num1 = sc.nextInt();
System.out.println("请输入第二个数字");
int num2 = sc.nextInt();
System.out.println("请输入第三个数字");
int num3 = sc.nextInt();
int max = 0;
max = num1>num2?num1:num2;
max = max>num3?max:num3;
System.out.println(max);
}
}
数组扩容
数组一旦定义长度不可变。
但可以通过创建新数组进行数组的扩容。
public void addArray(){
String[] arr1 = new String[1];
Scanner sc = new Scanner(System.in);
int i = 1;
while(true){
System.out.println("请输入第"+i+"名学生姓名:");
String name = sc.next();
if ("结束".equals(name)){
break;
}else{
/*第一次输入时不需要扩容直接插入*/
if (i==1){
arr1[i-1] = name;
}else{
/*数组长度不够需要扩容*/
String[] arr2 = new String[arr1.length +1];
for (i=0;i<arr1.length;i++){
arr2[i] = arr1[i];
}
arr2[arr2.length-1] = name;
arr1 = arr2;
}
i++;
}
}
System.out.println(Arrays.toString(arr1));
}
稀疏数组
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组.
稀疏数组的处理方法是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
public class Test {
public static void main(String[] args) {
//1.创建一个二维数组11*11 0:没有棋子 1:黑棋 2:白棋
int[][] arr = new int[11][11];
arr[1][2] = 1;
arr[2][3] = 2;
//输出原始的数组
System.out.println("输出原始的数组");
for (int[] ints : arr) {
for (int anInt : ints) {
System.out.print(anInt + " ");
}
System.out.println();
}
//转换为稀疏数组保存
//获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (arr[i][j] != 0) {
sum++;
}
}
}
System.out.println("有效值的个数:" + sum);
//2.创建一个稀疏数组的数组
int[][] arr2 = new int[sum + 1][3];
arr2[0][0] = 11;
arr2[0][1] = 11;
arr2[0][2] = sum;
//遍历二维数组,将非零的值,存放在稀疏数组中
int count = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[i][j]!=0){
count++;
arr2[count][0] = i;
arr2[count][1] = j;
arr2[count][2] = arr[i][j];
}
}
}
System.out.println("稀疏数组:");
for (int i = 1; i < arr2.length; i++) {
System.out.println(arr2[i][0]+" "+
arr2[i][1]+" "+
arr2[i][2]+" ");
}
//还原稀疏数组
//1.读取稀疏数组
int[][] arr3 = new int[arr2[0][0]][arr2[0][1]];
//2.给其中的元素还原它的值
for (int i = 1; i < arr2.length; i++) {
arr3[arr2[i][0]][arr2[i][1]] = arr2[i][2];
}
//打印还原
System.out.println("还原的数组");
for (int[] ints : arr3) {
for (int anInt : ints) {
System.out.print(anInt + " ");
}
System.out.println();
}
}
}
方法
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的代码块。
- 方法包含于类或对象中
构造方法
系统会给所有的类提供一个默认的无参构造方法,该方法是隐藏的,如果显式的写了自定义构造方法,那么系统默认的无参数构造方法就会销毁。
构造方法没有返回值(void都没有),并且方法名必须和类名一致。
- 用来创建对象,也就是对象在创建之前必须要执行构造方法。
修饰符 className(参数列表){
方法体;
}
定义
方法包含一个方法头和一个方法体。
四种访问修饰符:
- public:公共--可以在任意类任意包中访问
- private:私有--只允许在本类中访问
- protected:受保护的--允许在本类,本包,子类中访问
- 默认的--允许在本类,本包中访问
修饰符 返回值类型 方法名(参数类型 参数名){
方法体
return 返回值;
}
修饰符(可选):告诉编译器如何调用该方法,定义了该方法的访问类型。
返回值类型:方法可能会返回值,void方法没有返回值。
方法名:方法的实际名称,方法名和参数表共同构成方法签名。
参数类型:参数像是一个占位符,当方法被调用时传递值给参数,这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体:方法体包含具体的语句,定义该方法的功能。
形式参数:在方法被调用时用于接收外界输入的数据。
实际参数:调用方法时实际传给方法的数据。
调用
对象名.方法名(实参列表)
方法重写
重写需要有继承关系,子类重写父类的方法。
当子类继承至父类的方法不满足子类的业务逻辑时需要重写子类继承的该方法。
重写是方法的重写,和属性无关。
@Override:有功能的注释。
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大但不能缩小
public>protected>default>private
-
抛出的异常:范围可以被缩小但不能扩大。
方法重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法重载的规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同,类型不同,参数排列顺序不同等)
- 方法的返回类型可以相同或不同
- 返回类型不同不足以称为方法的重载
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等逐个匹配,选择对应的方法,匹配失败编译器会报错。
可变参数
在方法声明中,在指定参数类型后加一个省略号(...)
JDK1.5开始,Java支持传递同类型的可变参数给一个方法
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通的参数必须在它之前声明
//排序
public class Test {
public static void main(String[] args) {
printMax(34,3,5,9,7,6);
printMax(new double[]{1,2,3});
}
//输出最大值,不确定传递几个数
public static void printMax(double... numbers){
if (numbers.length == 0){
System.out.println("No argument passed");
return;//终止方法
}
double result = numbers[0];
//排序
for(int i = 1;i < numbers.length;i++){
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("The max value is "+ result);
}
}
递归
方法调用方法,自己调用自己
利用递归可以用简单的程序来解决一些复杂的问题。把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,用少量的程序描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
//阶乘
public class Test {
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n){
if (n == 1){
return 1;//结束方法,结束递归
}else{
//n*n-1 的阶乘
return n*f(n-1);
}
}
}
实用类
/*数学类,有许多的计算方法。*/
Math.floor();/*将数向下取整*/
Math.ceil();/*将数向上取整*/
Math.round();/*将数四舍五入*/
Math.radom();/*获得0-1的随机数*/
Random r1 = new Random();/*Random无参构造,系统默认的算法获取随机数*/
Random r2 = new Random(2222);/*Random有参构造,通过给定的种子,系统算出随机数,只要种子随机,获得的数据就随机*/
r1.nextInt();/*获取下一个随机数*/
/*
BigInteger:用来处理整数。
BigDecimal:通过字符串来表示浮点,涉及钱或者要求浮点计算精度时必须使用BigDecimal对象。
*/
BigDecimal b1 = new BigDecimal(2.0);
BigDecimal b2 = new BigDecimal(1.1);
BigDecimal result = b1.subtract(b2);
System.out.println("减法:"+result.doubleValue);
System.out.println("减法:"+b1.subtract(b2));
System.out.println("加法:"+b1.add(b2));
System.out.println("乘法:"+b1.multiply(b2));
System.out.println("除法:"+b1.divide(b2));
/*
RoundingMode:舍入模式
DOWN:舍入模式,零向舍入(舍弃后边所有,不向前进1)
UP:舍入模式,舍入去零,零不舍入。
HELF_DOWN:遵循四舍五入规则,大于5向前一位进1
HELF_UP:遵循四舍五入规则,大于等于5向前一位进1
*/
/*创建四舍五入的配置对象*/
MathContext mc1 = new MathContext(3,RoudingMode.HELF_UP);//保留3位有效数字
b1.setScale(3, RoundingMode.HALF_UP);//保留3位小数
BigDecimal b3 = new BigDecimal("3.1415926");
System.out.println(b3.round(mc1))//3.14
包装类
在Java中8中基本数据类型不属于对象,但是Java为基础数据类型都提供了对应的包装类。
/*
基本数据类型和包装类转换的方法(目前可以自动类型转换)
基本数据类型有不同的默认值。
包装类是对象,默认值全部是null
基本数据类型-->包装类
*/
byte-->Byte
short-->Short
int-->Integer
long-->Long
char-->Character
float-->Float
double-->Double
boolean-->Boolean
/*
提供了String和基本数据类型之间的互相转换。
String->包装类:通过各个包装类提供的构造方法实现。
String->基本数据类型:通过parseInt()方法实现。
*/
String str = '123';
Integer i = new Integer(str);//str被转换为int类型,但str内不为数字时会报错
int x1 = 99;
String x2 = x1 + '';//x1被转换为字符串类型
/*char的包装类Character*/
Character.isDigit():是否是数字
Character.isLetter():是否是字母
Character.isLowerCase():是否是小写字母
Character.reverseBytes():反转字节
/*提供了一些操作该类数据的基本方法。*/
compareTo():比较两个对象的大小
1:前面的对象比后面大
0:相等
-1:后面的对象比前面大
parseint():将字符串类型的参数转换为带符号的十进制整数
toBinaryString():将十进制转换为二进制
toOctalString():将十进制转换为八进制
toHexString():将十进制转换为十六进制
String
String对象:字符串不变,它们的值在创建后不能被更改。
每修改一次,都是生成了一个新的对象。
字符串也有下标,从0开始。
方法 | charAt() | compareTo() | equals() | equalsIgnoreCase() | indexOf() | replace() | replaceAll() | split() |
---|---|---|---|---|---|---|---|---|
作用 | 返回当前索引处的字符 | 按字典顺序比较字符串大小 | 比较内容 | 忽略大小写比较 | 返回字符串第一次出现的位置 | 替换 | 替换所有 | 根据正则匹配拆分字符串 |
方法 | substring() | toCharArray() | toUpperCase() | toLowerCase() | trim() | length() | isEmpty |
---|---|---|---|---|---|---|---|
作用 | 根据指定范围截取 | 转换为字符数组 | 转大写 | 转小写 | 去前后空格 | 返回字符串长度 | 判断字符串是否为空 |
-
给一个字符串,统计每个字符出现的次数。
public void statistics(){ String str = "leeString"; /*在统计一个字符前判断该字符有没有出现过,如果没有出现过说明已经统计过*/ for (int i = 0; i < str.length(); i++) { char c = str.charAt(i);//接收字符 int num = 0;//计算出现次数 boolean flag = true;//判断是否出现过 //判断i之前有没有出现该字符 for (int y = 0; y < i; y++) { if (c==str.charAt(y)){ //如果出现过了,结束这个字符的循环 flag = false; break; } } //如果flag为true说明没有出现过,需要统计 if (flag) { //将字符和字符串中每一个字符比较 for (int n = 0; n < str.length(); n++) { if (c == str.charAt(n)) { num++; } } System.out.println(c + "出现了" + num + "次"); } }
StringBuffer
StringBuffer:线程安全的带缓冲区的可变字符串。
相当于字符串拼接,不会生成新的对象。
遇到大量字符串处理一定要使用StringBuffer
public void stringBuffer() {
String str = new String("abc");//创建两个对象:‘abc’、‘String’
String str1 = "abc";//创建一个对象:‘abc’
str1 = str+str1;//创建一个新对象:‘abcabc’
StringBuffer sb = new StringBuffer(str1);
sb.append(str);
System.out.println(sb);
//删除
sb.delete(2,3);
System.out.println(sb);
//插入
sb.insert(2,"中国");
System.out.println(sb);
//替换
sb.replace(3,5,"bababa");
System.out.println(sb);
//效率测试
//Date对象获取当前电脑时间
System.out.println("String拼接开始:"+new Date());
String strs="";
for (int i=0;i<50000;i++){
strs+=i;
}
System.out.println("String拼接结束:"+new Date());
System.out.println("StringBuffer拼接开始:"+new Date());
System.out.println("StringBuffer");
StringBuffer sb1 = new StringBuffer("");
for (int i=0;i<50000;i++){
sb1.append(i);
}
System.out.println("StringBuffer拼接结束:"+new Date());
}
异常
基类java.lang.Throwable作为所有异常的超类。
Exception:异常,java中所有异常的父类
Error:错误。
编译器异常:除了运行时异常的其他异常:IOException以及子类, SQLException以及子类, ParseException以及子类。
运行时异常:RuntimeException以及其子类都是运行时异常
程序运行时,情况不是非常完美的。用户输入不一定符合要求、打开文件可能不存在或者文件格式不对,读取的数据是空的等等。
-
对于不确定的代码,也可以加上try-catch,处理潜在的异常。
-
在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常。
-
尽量添加finally语句块去释放占用的资源
异常处理
-
抛出异常:throw、throws
-
捕获异常:try、catch、finally
import java.util.Scanner;
public class TestException {
//异常处理
public void testException() {
System.out.println("请输入数字:");
Scanner sc = new Scanner(System.in);
/*
* try中程序可能出错,如果出错跳到catch执行
* throws抛出异常并没有被处理,主函数类似的方法最好不要抛出异常
* finally最终处理,写在try-catch后,不能单独使用,这块代码不论有没有出现异常都会执行,可以用来进行垃圾回收
* */
try{
//认为有异常的代码块
int i = sc.nextInt();
System.out.println("数字是:"+i);
}catch (Exception e){
System.out.println("error:"+e);
}finally{
sc.close();
}
}
}
包机制
为了更好的组织类,Java提供了包机制,用于区别类名的命名空间。
为了能够使用某一个包的成员,需要在程序中导入该包。
package ... //使用该包下的所有对象
import ... //导入该包下的所有对象
单例模式
单例模式:内存中只能存在该类的一个实例。减少内存的消耗。
实现方法:
- 私有化构造方法。
- 定义一个私有的静态的本类变量,并且实例化。
- 提供一个公共的静态的方法返回本类类型的实例。
public class TestSingleton {
private static TestSingleton sl = new TestSingleton();
private TestSingleton(){}//私有化无参构造方法
public static TestSingleton init(){
return sl;
}
public void eat(){
System.out.println("吃的真饱!");
}
public static void main(String[] args) {
TestSingleton s1 = TestSingleton.init();
TestSingleton s2 = TestSingleton.init();
s1.eat();
s2.eat();
System.out.println(s1==s2);//判断对象内存地址值。一致。
}
}
JVM
Java程序运行在JVM虚拟机中,从内存中获取一块空间用于支持程序的执行。
内存:JVM中的栈包括Java虚拟机栈和本地方法栈,Java虚拟机栈为JVM执行Java方法服务。
栈:用来放局部变量基本数据类型和值,当创建一个方法时,生成一个栈帧,存放局部变量。
- 本地方法栈则为JVM使用到的Native方法服务。
- 栈是限定仅在表头进行插入和删除操作的线性表,入栈和出栈都是对栈顶元素进行操作的,所以栈是后进先出的。
- 栈是线程私有的,生命周期与栈相同。每个线程都会分配一个栈的空间。每个线程有独立的栈空间。
堆:堆用来放全局变量和对象。
- 当创建一个对象时,在堆里获得一块空间,存放该对象全局变量,类信息,生成一个内存地址池跟栈里变量的引用关联。
程序计数器:一块较小的内存空间,是当前线程所执行字节码的行号指示器。
-
一个线程的执行,是通过字节码解释器改变当前线程的计算器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。
-
每个线程都有一个独立的程序计数器,互不影响,独立存储,是线程私有的内存。
方法区:常量池和类信息。
内部类
内部类就是在一个类的内部再定义一个类。
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
一个Java类中可以有多个class类,但是只能有一个public class
Java代码规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰原则:lastName
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则:Man,GoodMan
- 方法名:首字母小写和驼峰原则:run(),runRun()
- 一般利用公司域名倒置作为包名。
- 设置方法时,保持方法的原子性,即一个方法只完成一个功能,利于后期的扩展。