Java基础知识(纯干货)
IDEA 开发 Java项目
卸载JDK
-
删除Java的安装目录
-
删除JAVA_HOME
-
删除path下关于java的目录
-
java -version
安装JDK17
-
下载链接:https://download.oracle.com/java/17/archive/jdk-17.0.1_windows-x64_bin.exe
-
设置环境变量
- 在我的电脑->高级系统设置新建变量 JAVA_HOME,变量值为 E:\Java (此处填写自己的安装路径)
- 新建CLASSPATH 变量值为 %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
- 在Path中新增变量 --- %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
- 测试是否安装成功 --- 在控制台输入 java 或者 java -version 不报错就可以了
安装JDK 8
-
百度搜索JDK8,找到下载地址
-
同意协议
-
下载对应版本
-
记住安装路径
-
配置环境变量
-
我的电脑-->右键-->属性-->高级系统设置
-
环境变量-->系统变量-->JAVA_HOME
-
配置Path变量----> %JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
-
-
测试是否安装成功
IDEA 代码快捷键
-
psvm 生成main方法
-
sout 输出 System.out.println(); 语句
-
Ctrl + D :复制当前行到下一行
-
alt + insert :会生成构造器(构造方法)
-
Ctrl + H :显示类结构图
-
快捷键 : Ctrl + ALT + T ---> 选择代码块进行包裹代码(如:if/else , try/catch......)
-
maven导依赖,浏览器直接搜 maven + mysql / standard 等等
Java语法
注释
-
单行注释: //
-
多行注释: /* 注释内容*/ , 可以注释一段文字
-
文档注释:(JavaDoc) /** 注释内容 */
标识符
-
标识符大小写敏感
-
应以字母、美元符、下划线开始
-
不可使用关键字作为变量名或方法名
数据类型
-
注意:
-
long 类型要在数字后面加个L
-
float类型要在数字后面加个F
-
String 用来定义字符串,String不是关键字
-
布尔型:
-
boolean flag = true; boolean flag = false;
-
-
什么是字节
位(bit):是计算机 内部数据 存储的最小单位,11001100是一个八位二进制数。
字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写B来表示。
字符:是指计算机中使用的字母、数字、字和符号
1 bit 表示1位 1Byte表示一个字节---> 1B(byte) = 8 bit
1024B=1KB 1024KB=1M 1024M=1G 1024G=1TB
数据类型扩展
字符扩展
//所有的字符本质还是数字
//编码 Unicode 表: (97 = a 65 = A) 2字节 0-65536 Excel 2的16次方 = 65536
//U0000 UFFFF
char c3 = '\u0061';
System.out.println(c3);
//转义字符 \t-->制表符 \n-->换行
System.out.println("Hello\nWorld!");
System.out.println("=================================================");
String sa = new String("hello world");
String sb = new String("hello world");
System.out.println(sa==sb); //false
String sc = "hello world";
String sd = "hello world";
System.out.println(sc==sd); //true
//对象 从内存分析
//布尔值扩展
boolean flag = true;
if (flag==true){ } //新手
if (flag){ } //老手
//Less is More! 代码要精简易读
类型转换
-
//强制转换 (类型)变量名 高--低
int i = 128;
byte b = (byte)i;
System.out.println(i); //128
System.out.println(b); // -128 -
//自动转换 低--高
int m = 128;
double n = m;
System.out.println(m); //128
System.out.println(n); //128.0 -
注意:
-
不能对布尔值进行转换
-
不能把对象类型转换为不相干的类型
-
在把高容量转换为低容量的时候,强制转换
-
转换的时候可能存在内存溢出,或者精度问题
-
-
char c = 'a';
int d = c+1;
System.out.println(d); // 98
System.out.println((char) d); // b -
public static void main(String[] args) {
//操作数比较大的时候,注意溢出问题
//JDK7新特性,数字之间可用下划线分割
int money = 10_0000_0000; int years = 20;
int total = money * years; // -1474836480 , 计算的时候溢出了
long total2 = money * years; //默认是int,转换之前已经存在问题
long total3 = money*((long)years); //先把一个数转换为 long
System.out.println(total3); //20000000000
}
变量
-
Java是一种强类型语言,每个变量都必须声明其类型。类型可以是基本类型,也可以是引用类型。
-
Java变量是程序中最基本的存储单元,其要素包括变量名、变量类型和作用域。
-
变量名必须是合法的标识符。
变量作用域
1. 类变量 2.实例变量 3.局部变量
public class Variable {
static int allClicks = 0; //类变量
String str = "Hello world"; //实例变量
public void method(){
int i = 0; //局部变量
}
}
变量使用---示例
public class Demo04 { //类变量 static static double salary = 2500; //属性:变量 //实例变量:从属于对象; 如果不进行初始化,则输出该类型的默认值 //布尔值:默认是false //除了基本类型,其余的默认值都是null String name; int age; //main方法 public static void main(String[] args) { //局部变量-->必须声明和初始化值 int i = 10; System.out.println(i); //实例变量:对应上面的内容 //变量类型 变量名字 = new Demo04(); Demo04 demo04 = new Demo04(); System.out.println(demo04.age); System.out.println(demo04.name); //类变量 static System.out.println(salary); } //其他方法 public void add(){ } }
变量的命名规范
-
类成员变量:首字母小写和驼峰原则:除了第一个单词以外,后面的单词首字母大写。如:monthSalary.
-
局部变量:首字母小写和驼峰原则
-
常量:大写字母和下划线:MAX_VALUE
-
类名:首字母大写和驼峰原则:Man,GoodMan
-
方法名:首字母小写和驼峰原则:run(),runRun()
常量
-
常量名一般使用大写字符。
-
格式: final 常量名 = 值 ; 例: final double PI = 3.14 ;
-
修饰符(数据类型前的内容,如示例中的 static 和 final ),不存在先后顺序。
static final double PI = 3.14;
与final static double PI = 3.14;
输出内容相同。
运算符
-
数据类型没有 long 时,所有 非int 类型转为 int 型:
1 public static void main(String[] args) { 2 long a = 1234569653412L; 3 int b = 123; 4 short c = 10; 5 byte d = 8; 6 System.out.println(a+b+c+d); //结果为 Long 型 7 System.out.println(b+c+d); //结果为 Int 型 8 System.out.println(c+d); //结果为 Int 型 9 }
-
运算结果的数据类型为参与运算的变量中最高优先级的数据类型。
-
关系运算符返回的结果为:正确 / 错误 布尔值
幂运算
------借助Math类
//幂运算 2^3 2*2*2=8
double pow = Math.pow(2,3);
System.out.println(pow); // 8.0
位运算符
-
& | ^ ~
//位运算符
/** A = 0011 1100
* B = 0000 1101
* -------------------
* A&B = 0000 1100
* A|B = 0011 1101
* A^B = 0011 0001
* ~B = 1111 0010*
*/
-
<< 相当于 * >> 相当于 /
三元运算符
格式: x ? y : z 如果 x==true,则结果为 y,否则结果为 z
int score = 50;
String type = score < 60 ? "不及格":"及格";
System.out.println(type); // 结果为 不及格
运算符优先级
优先级 | 描述 | 运算符 |
---|---|---|
1 | 括号 | ()、[] |
2 | 正负号 | +、- |
3 | 自增自减,非 | ++、--、! |
4 | 乘除,取余 | *、/、% |
5 | 加减 | +、- |
6 | 移位运算 | <<、>>、>>> |
7 | 大小关系 | >、>=、<、<= |
8 | 相等关系 | ==、!= |
9 | 按位与 | & |
10 | 按位异或 | ^ |
11 | 按位或 | | |
12 | 逻辑与 | && |
13 | 逻辑或 | || |
14 | 条件运算 | ? : |
15 | 赋值运算 | =、+=、-=、*=、/=、%= |
16 | 位赋值运算 | &=、|=、<<=、>>=、>>>=、 |
字符串连接符 +
int m = 10; int n = 20; //字符串连接符 + System.out.println(""+m+n); // 结果为 1020 System.out.println(m+n+""); // 结果为 30
JavaDoc生成文档
1. 在IDEA中生成一个Javadoc文档
-
首先新建一个文件夹,用于存放要生成的Javadoc文档;
-
点击 IDEA 顶部的Tools菜单,选择Generate JavaDoc选项;
-
然后在弹出界面,点击 Output directory 输入框后面的按钮,选择刚才新建的文件夹;
-
在底部的Locale输入框配置语言和编码集,语言用zh_CN,代表中文
-encoding utf-8 -charset utf-8
-
点击确定后,等待 IDEA 生成JavaDoc文档即可,
-
接下来打开JavaDoc文件夹,找到index.html文件,点击就能看到API文档。
2. 在IDEA中生成一个Javadoc文档
参见 https://blog.csdn.net/Joy_Roy6/article/details/123028798 (Ctrl+鼠标左键)
Java流程控制
Scanner对象
-
可以通过Scanner类来获取用户的输入。
-
基本语法:
Scanner s = new Scanner (System.in);
-
通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要使用 hasNext() 与hasNextLine() 判断是否还有输入的数据。
-
next() 与 nextLine() 的区别:
-
next方式:
1 public static void main(String[] args) { 2 //创建一个扫描器对象,用于接收键盘数据 3 Scanner scanner = new Scanner(System.in); //程序会等待用户输入完毕 4 5 System.out.println("使用next方式接收:"); //判断用户有没有输入字符串 6 7 if (scanner.hasNext()){ 8 //使用next方式接收 9 String str = scanner.next(); 10 System.out.println("输入的内容为:"+str); 11 } 12 //凡是属于IO流的类,如果不关闭,会一直占用资源 13 scanner.close(); 14 }
-
也可以通过不使用 if() 判断,来获取用户输入的内容:
1 public static void main(String[] args) { 2 Scanner scanner = new Scanner(System.in); 3 System.out.println("请输入数据:"); 4 5 String str = scanner.nextLine(); 6 System.out.println("输出的内容为:"+str); 7 8 scanner.close(); 9 }
-
小例子:
1 public static void main(String[] args) { 2 //输入多个数字,并求其总和与平均数, 3 //每输入一个数字用回车键确认,通过输入非数字来结束输入并输出执行结果 4 Scanner scanner = new Scanner(System.in); 5 //和 6 double sum = 0; 7 //计算输入了多少个数字 8 int m = 0; 9 //通过循环判断是否还有输入,并在里面对每一次进行求和与统计 10 while(scanner.hasNextDouble()){ 11 double x = scanner.nextDouble(); 12 m = m + 1; 13 sum = sum + x; 14 System.out.println("你输入了第"+m+"个数据,当前结果sum="+sum); 15 } 16 System.out.println(m + "个数的和为" + sum); 17 System.out.println(m + "个数的平均值是" + (sum / m)); 18 scanner.close(); 19 }
顺序结构
选择结构
1. if 选择结构
2. switch 多选择结构
-
Switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
-
Switch 语句中的变量类型可以是 byte, short, int 或char,String , 同时case 标签必须为字符串常量或字面量。
1 switch (){ 2 case value : 3 //语句 4 break; //可选 5 case value : 6 //语句 7 break; //可选 8 //可以有任意数量的case语句 9 default : //可选 10 //语句 11 }
循环结构
1. while 循环
while 是最基本的循环,它的结构为:
while(布尔表达式){
//循环内容
}
-
只要布尔表达式为true,循环就会一直执行下去。
-
下面代码计算 1+2+3+...+100:
1 public static void main(String[] args) { 2 int i = 0; 3 int sum = 0; 4 while (i<=100){ 5 sum = sum + i; 6 i++; 7 } 8 System.out.println(sum); 9 }
2. do...while 循环
do...while 循环和 while 循环相似,不同的是,do...while循环至少会执行一次。
do {
//代码语句
}while(布尔表达式);
-
下面代码计算 1+2+3+...+100:
1 public static void main(String[] args) { 2 int i = 0; 3 int sum = 0; 4 do { 5 sum = sum + i; 6 i++; 7 }while(i<=100); 8 System.out.println(sum); 9 }
3. For循环
-
for 循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。
-
for 循环执行的次数是在执行前就确实的。
-
语法格式如下:
for (初始化;布尔表达式;更新){
//代码语句
}
-
练习:输出1-1000之间能被5整除的数,并且每行输出3个
1 public static void main(String[] args) { 2 for (int i = 0; i <= 1000; i++) { 3 if(i%5==0){ 4 System.out.print(i+"\t"); 5 } if (i%(5*3)==0){ //每行 6 System.out.println(); 7 //System.out.println("\n"); 8 } 9 } 10 }
-
练习:打印九九乘法表
//1.先打印第一列
//2.把固定的1再用一个循环包起来
//3.去掉重复项,i <= j
//4.调整样式
1 public static void main(String[] args) { 2 for (int j = 1; j <= 9; j++) { 3 for (int i = 1; i <= j; i++) { 4 System.out.print(j+"*"+i+"="+(j*i) + "\t"); 5 } 6 System.out.println(); 7 } 8 }
4.增强for循环
-
语法格式:
for(声明语句:表达式){
//代码句子
}
-
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
-
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
-
练习:遍历数组元素---以下为两种方法,法一为普通for循环,法二为增强for循环。
1 public static void main(String[] args) { 2 int [] numbers = {10,20,30,40,50}; //定义了一个数组 3 4 for (int i = 0; i < 5; i++) { 5 System.out.println(numbers[i]); 6 } 7 System.out.println("-----------------------------------------------------"); 8 //遍历数组的元素 9 for(int x:numbers){ 10 System.out.println(x); 11 } 12 }
break 与 continue
-
break 用于强行退出循环,不执行循环中剩余的语句。( break 语句也在switch 语句中使用)。
break 在任何循环语句的主体部分,均可用 break 控制循环的流程。
-
continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
流程控制练习
打印三角形
public static void main(String[] args) { //打印三角形 5行 for (int i = 1; i <= 5; i++) { for (int j = 5; j >= i ; j--) { System.out.print(" "); } for (int j = 1; j <= i ; j++) { System.out.print("*"); } for (int j = 1; j < i; j++) { System.out.print("*"); } System.out.println(); } }
Debug的使用
Java 方法详解
方法的定义
-
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段。
-
一般情况下,定义一个方法包含以下语法:方法包含一个方法头和一个方法体。
-
定义方法的格式:
修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; }
public class Demo01 { //main方法 public static void main(String[] args) { //实际参数:实际调用传递给他的参数 int sum = add(1, 2); //下面的add方法被static修饰时,main方法才可调用add() System.out.println(sum); } //加法 //形式参数,用来定义作用的 public static int add(int a,int b){ return a+b; } }
方法调用
-
调用方法:对象名.方法名(实参列表)
-
若是 static 即为静态方法,可以直接通过类名来调用;
-
举例:
-
-
若为非静态方法,先用 new 实例化该类,然后进行调用;
-
举例:
-
-
两种调用方法的方式:
-
当返回一个值的时候,方法调用通常被当作一个值。例如:
int larger = max(30, 40);
-
如果方法返回值是void,方法调用一定是一条语句。如:
System.out.println("Hello,World!");
方法的重载
-
定义:方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数类型或参数的个数。
-
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
-
方法重载的规则:
-
方法名称必须相同;
-
参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等);
-
方法的返回类型可以相同,也可以不同;
-
仅仅返回类型不同不足以成为方法的重载。
可变参数
-
在方法声明中,在指定参数类型后加一个省略号(...)。
-
一个方法只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
1 public class Demo04 { 2 public static void main(String[] args) { 3 //调用可变参数的方法 4 printMax(34, 3, 3, 2, 66.6,68.68); 5 printMax(new double[]{1,2,3}); } 6 public static void printMax(double... numbers){ 7 if(numbers.length == 0) { 8 System.out.println("No argument passed"); 9 return; 10 } 11 double result = numbers[0]; 12 //排序! 13 for (int i = 1;i < numbers.length; i++){ 14 if(numbers[i] > result){ 15 result = numbers[i]; 16 } 17 } 18 System.out.println("The max value is " + result); 19 } 20 }
递归(笔试高频问点)
-
递归就是:A方法调用A方法,即自己调用自己。
-
递归结构包括两个部分:
-
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
-
递归体:什么时候需要调用自身方法。
1 public class Demo06 { 2 //递归思想 3 //求阶乘 5! 5*4*3*2*1 4 public static void main(String[] args) { 5 System.out.println(f(5)); 6 } 7 public static int f(int n){ 8 if (n == 1){ 9 return 1; 10 }else { 11 return n * f(n-1); 12 } 13 } 14 }
数组
数组的定义
-
数组是相同类型数据的有序集合。
-
相同类型的若干个数据,按照一定的先后次序排列组合而成。
-
每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
-
数组也是对象。数组元素相当于对象的成员变量。
-
数组长度是确定的,不可变得。如果越界,则报错:ArrayIndexOutOfBoundsException
数组的声明创建
-
首先必须声明数组变量,才能在程序中使用数组。
-
声明数组变量的语法如下:
dataType[] arrayRefVar;
-
Java语言使用new操作符来创建数组,语法如下:
dataType[] arrayRefVar = new dataType[arraySize];
-
数组的元素是通过索引访问的,数组索引从0开始。
-
获取数组长度:
arrays.length
1 public class ArrayDemo01 { 2 //变量的类型 变量的名字 = 变量的值 3 //数组类型 4 public static void main(String[] args) { 5 int[] nums; //1.声明一个数组 6 nums = new int[10]; //2.创建一个数组 7 //上面两句也可合并为一句:int[] nums2 = new int[10]; 8 //3.给数组元素中赋值 9 nums[0] = 1; nums[1] = 2; 10 nums[2] = 3; nums[3] = 4; 11 nums[4] = 5; nums[5] = 6; 12 nums[6] = 7; nums[7] = 8; 13 nums[8] = 9; nums[9] = 10; 14 //计算所有元素的和 15 int sum = 0; 16 for (int i = 0; i < nums.length; i++) { 17 sum = sum + nums[i]; 18 } 19 System.out.println("总和为:"+ sum); 20 } 21 }
数组的三种初始化
静态初始化
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
动态初始化
int[] a = new int[2]; a[0]=1; a[1]=2;
数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
1 public class ArrayDemo02 { 2 public static void main(String[] args) { 3 //静态初始化:创建 + 赋值 4 int[] a = {1,2,3,4,5,6,7,8}; 5 System.out.println(a[0]); 6 //动态初始化:包含默认初始化 7 int[] b = new int[10]; 8 b[0] = 10; 9 System.out.println(b[0]); 10 } 11 }
数组的使用
-
普通的for循环
-
For-Each循环
-
数组作方法入参
-
数组作返回值
1 public class ArrayDemo04 { 2 public static void main(String[] args) { 3 int[] arrays = {1,2,3,4,5}; 4 //增强的for循环 5 //for (int array : arrays) { 6 // System.out.println(array); 7 //} printArray(arrays); 8 int[] reverse = reverse(arrays); 9 printArray(reverse); 10 } 11 //打印数组元素 12 public static void printArray(int[] arrays){ 13 for (int i = 0; i < arrays.length; i++) { 14 System.out.println(arrays[i]+""); 15 } 16 } 17 18 //反转数组---即倒序输出数组元素 19 public static int[] reverse(int[] arrays){ 20 int[] result = new int[arrays.length]; 21 //反转的操作 22 for (int i = 0, j = result.length-1; i < arrays.length; i++,j--) { 23 result[j] = arrays[i]; 24 } 25 return result; 26 } 27 }
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其中每个元素都是一个一维数组。
二维数组
int a[][] = new int[3][5];
解析:以上二维数组a可以看成一个三行五列的数组。
1 public class ArrayDemo05 { 2 public static void main(String[] args) { 3 //[4][2] 4 int[][] array = {{1,2},{2,3},{3,4},{4,5}}; 5 System.out.println(array[0][0]); 6 System.out.println(array[0][1]); // 1 2 7 System.out.println("================"); 8 System.out.println(array.length); // 4 9 System.out.println(array[0].length); // 2 10 } 11 }
Arrays 类
-
数组的工具类 java.util.Arrays
-
Arrays 类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用
-
具有以下常用功能:
-
给数组赋值:通过 fill 方法
-
对数组排序:通过 sort 方法
-
比较数组:通过 equals 方法比较数组中元素值是否相等
-
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作
-
可借助 JDK 帮助文档进行使用相关内容
-
1 public static void main(String[] args) { 2 int[] a = {1,2,3,4,9898,36523,498561,4512}; 3 System.out.println(a); 4 //打印数组元素 Arrays.toString 5 Arrays.sort(a); //数组进行排序-升序 6 System.out.println(Arrays.toString(a)); 7 8 Arrays.fill(a,2,4,0); //数组填充 此处2和4表示从第二位到第四位填充为0 9 System.out.println(Arrays.toString(a)); 10 }
冒泡排序
冒泡排序代码:两层循环,外层冒泡轮数,里层依次比较。时间复杂度为O(n2)
1 public static void main(String[] args) { 2 int[] a = {5,158,45,6,89,68,12,39,56,45,53}; 3 int[] sort = sort(a); //调用完自己写的排序方法以后,返回一个排序后的数组 4 System.out.println(Arrays.toString(sort)); 5 } 6 7 //冒泡排序 8 //1. 比较数组中,两个相邻的元素,如果第一个数比第二个数大,就交换二者位置 9 public static int[] sort(int[] array){ 10 //临时变量 11 int temp = 0; 12 //外层循环,判断要走多少次; 13 for (int i = 0; i < array.length-1; i++) { 14 boolean flag = false; //通过flag标识位减少无意义的比较 15 //内层循环,比较判断两个数,若第一个数比第二个数大,则交换位置 16 for (int j = 0; j < array.length-1-i; j++) { 17 if (array[j+1] < array[j]){ 18 temp = array[j]; 19 array[j] = array[j+1]; 20 array[j+1] = temp; 21 flag = true; 22 } 23 } 24 if (flag == false){ 25 break; 26 } 27 } 28 return array; 29 } 30 }
稀疏数组
-
当一个数组中大部分元素为0时,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
-
稀疏数组的处理方式是:
-
记录数组一共有几行几列,有多少个不同值
-
把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
1 public class ArrayDemo08 { 2 public static void main(String[] args) { 3 //1. 创建一个二维数组 11*11 0:没有棋子 1:黑棋 2:白棋 4 int[][] array1 = new int[11][11]; 5 array1[1][2] = 1; array1[2][3] = 2; 6 //输出原始数组 7 System.out.println("输出原始的数组"); 8 for (int[] ints : array1) { //快捷方式: array1.for + 回车 9 for (int anInt : ints) { //快捷方式: ints.for + 回车 10 System.out.print(anInt+"\t"); 11 } 12 System.out.println(); 13 } 14 System.out.println("==================================="); 15 //转换为稀疏数组保存 16 //获取有效值的个数 17 int sum = 0; 18 for (int i = 0; i < 11; i++) { 19 for (int j = 0; j < 11; j++) { 20 if (array1[i][j] != 0){ sum++; } 21 } 22 } 23 System.out.println("有效值的个数为:"+sum); 24 //2. 创建一个稀疏数组的数组 25 int[][] array2 = new int[sum+1][3]; 26 array2[0][0] = 11; array2[0][1] = 11; array2[0][2] = sum; 27 //遍历二维数组,将非零的值,存放稀疏数组中 28 int count = 0; 29 for (int i = 0; i < array1.length; i++) { 30 for (int j = 0; j < array1[i].length; j++) { 31 if (array1[i][j] != 0){ 32 count++; 33 array2[count][0] = i; 34 array2[count][1] = j; 35 array2[count][2] = array1[i][j]; 36 } 37 } 38 } 39 //输出稀疏数组 40 System.out.println("稀疏数组:"); 41 for (int i = 0; i < array2.length; i++) { 42 System.out.println(array2[i][0]+"\t" 43 +array2[i][1]+"\t" 44 +array2[i][2]+"\t"); 45 } 46 System.out.println("==================================="); 47 System.out.println("还原:"); 48 //1. 读取稀疏数组的值 49 int[][] array3 = new int[array2[0][0]][array2[0][1]]; 50 //2.给其中的元素还原它的值 51 for (int i = 1; i < array2.length; i++) { 52 array3[array2[i][0]][array2[i][1]] = array2[i][2]; 53 } 54 //3. 打印 55 System.out.println("输出还原的数组"); 56 for (int[] ints : array3) { //快捷方式: array1.for + 回车 57 for (int anInt : ints) { //快捷方式: ints.for + 回车 58 System.out.print(anInt+"\t"); 59 } 60 System.out.println(); 61 } 62 } 63 }
正式篇
面向对象编程
-
本质:以类的方式组织代码,以对象的组织(封装)数据。
-
三大特性:封装、继承、多态。
回顾方法及加深
-
方法的定义:
-
修饰符、返回值类型
-
break:跳出Switch,结束整个循环
-
return:结束方法,返回一个结果
-
方法名:注意规范,见名知意
-
参数列表:(参数类型,参数名)...定义可变常参数
-
异常抛出: 后面讲解
-
-
方法的调用:递归
-
静态方法
-
非静态方法
-
形参和实参
-
值传递和引用传递的区别:
this 关键字
-
创建与初始化对象
-
使用 new 关键字创建对象
-
使用 new 关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象 进行默认的初始化以及对类中构造器的调用
-
一个类中就只有两部分:属性 和 方法
//类是抽象的,需要实例化 //类实例化后会返回一个自己的对象 //student对象就是一个Student类的具体实例
构造方法(构造器)
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。
-
并且构造器有以下两个特点:
-
必须和类的名字相同
-
必须没有返回类型/值,也不能写 void
-
作用:
-
使用 new 关键字,本质是在调用构造
-
构造器一般用来初始化值
-
快捷键: alt + insert 生成构造器(构造方法)
生成有参构造方法:alt + insert ---> constructor ---> 选择需要生成的 ---> OK
生成无参构造方法:alt + insert ---> constructor ---> 选择需要生成的 ---> select none
创建对象内存分析
参见 狂神视频 面向对象P65,即视频 面向对象06
简单小结类与对象
-
类与对象: 类是一个模板:抽象,对象是一个具体的实例
-
方法:定义、调用
-
对象的引用:
-
引用类型:基本类型(8种),对象是通过引用来操作的:栈--->堆
-
属性:字段(Field/成员变量)
默认初始化:数字: 0 0.0 char:u0000
boolean:false 引用:null
属性的定义: 修饰符 属性类型 属性名 = 属性值
-
对象的创建和使用
-
必须使用 new 关键字创造对象,创造对象还需要构造器
Person xiaoming = new Person();
-
对象的属性 xiaoming.name
-
对象的方法 xiaoming.sleep()
-
类:一个类中就只有两部分:属性 和 方法
静态的属性 即 属性
动态的行为 即 方法
封装
-
该露的露,该藏的藏
我们程序设计要追求 “高内聚,低耦合” 。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
-
封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
-
属性私有,get / set
-
封装的意义:提高程序的安全性,保护数据; 隐藏代码的实现细节; 统一接口; 系统的可维护性增加了。
-
-
有时还需考虑数据的合理性,如下图。图中设置年龄不太符合实际,便在右侧Student类中的setAge里进行相应限制条件的设置:
继承
-
继承的本质是对某一批类的抽象,子类继承了父类,就会拥有父类的全部方法。
-
extends 意为 “扩展”。子类是父类的扩展。
-
Java中,类只有单继承,没有多继承!
-
继承是类与类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。
-
私有的东西无法被继承
Object 类
-
Java中,所有的类都默认直接或间接继承 Object 类。
super 关键字
-
super 调用父类的构造器,必须要在子类构造器的第一行。
-
注意点:
-
super 调用父类的构造方法,必须在构造方法的第一行;
-
super 必须只能出现在子类的方法或者构造方法中;
-
super 和 this 不能同时调用构造方法。
-
-
super 与 this 的区别:
-
代表的对象不同:
this :代表 本身调用者这个对象
super : 代表 父类对象的应用
-
前提 :
this:没有继承也可以使用
super:只能在继承条件才可以使用
-
构造方法:
this( ):调用本类的构造
super( ):调用父类的构造
-
方法重写
-
重写都是方法的重写,和属性无关。
-
重写只跟非静态方法(成员方法)有关,而与静态方法无关;
-
静态方法和非静态方法是不一样的:
在静态方法中,方法的调用只和左边声明的对象类型有关,而与右边无关,是哪个类型,就调用对应的方法。
-
注意:重写需要有继承关系,子类重写父类的方法
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大,但不可以缩小: public > Protected > Default > private
-
抛出的异常:范围可以被缩小,但不能扩大:ClassNotFoundException --> Exception (大)
-
-
重写,子类的方法和父类必须要一致,但方法体不同。
-
为什么需要重写:
-
父类的功能,子类不一定需要,或者不一定满足。 Alt + Insert : @Override
-
多态
-
多态,即同一种方法可以根据发送对象的不同而采用多种不同的行为方式。
-
多态注意事项:
-
多态是方法的多态,属性没有多态
-
父类和子类,有联系 类型转换异常--->ClassCastException
-
存在的条件: ①有继承关系;②(子类重写父类方法)方法需要重写;
③父类引用指向子类对象---> Father f1 = new Son( );
-
-
不能重写的方法:
-
static 方法,属于类,不属于实例;
-
final 是常量;
-
private 方法,私有方法。
-
-
Application.java
1 package oop; 2 import oop.demo06.Person; 3 import oop.demo06.Student; 4 5 public class Application { 6 public static void main(String[] args) { 7 //一个对象的实际类型是确定的 // 可以指向的引用类型就不确定了 8 //Student 能调用的方法都是自己的,或者继承父类的 9 Student s1 = new Student(); 10 //Person 是父类,可以指向子类,但是不能调用子类独有的方法 11 Person s2 = new Student(); //父类的引用指向子类 12 Object s3 = new Student(); 13 14 //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大 15 s2.run(); //子类重写了父类的方法,执行子类的方法 16 s1.run(); 17 // s2.eat(); // s2前面是Person,但Person类里没有eat(),所以此处不能使用该句 18 } 19 }
Person.java
1 package oop.demo06; 2 public class Person { 3 public void run(){ 4 System.out.println("run"); 5 } 6 }
Student.java
1 package oop.demo06; 2 public class Student extends Person{ 3 @Override 4 public void run() { 5 System.out.println("son"); 6 } 7 public void eat(){ 8 System.out.println("eat"); 9 } 10 }
instanceof
-
instanceof 是 Java 的保留关键字。
-
它的作用是,测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
-
System.out.println( X instanceof Y);
能不能编译通过,取决于 X 与 Y 之间是否存在父子关系。
类型转换
1.
public static void main(String[] args) { //类型之间的转化: 父 子 //高 ---> 低 Person obj = new Student(); //student 将这个对象转换为Student类型, // 我们就可以使用Student类型的方法了 ((Student)obj).go(); }
-
父类引用指向子类的对象
-
子转父,不可用子类特有方法,可获得子类重写父类的方法,可获得父类独有方法。
以下强制转换/自动转换不知道对否,待验证。
-
把子类转换为父类,由低到高,向上转型,自动转换
-
把父类转换为子类,向下转型:需要强制转换。 会丢失父类被子类所重写掉的方法。
父类转子类会丢失子类特有的一些方法
-
以上3、4点可以类比基本数据类型的转换:
-
double 转 int ,丢失小数;int 转 double ,强制转换
static 关键字详解
-
静态变量输出时,可以直接用 类名.变量名 来输出。
-
加了 static 可以不创建对象,而使用类名.属性名或者方法名 直接调用。只是在本类里可以省去类名。
-
静态变量对于类,所有对象(实例)所共享,当直接使用类去调用得到说明这个变量是静态的。
-
类的加载顺序:静态代码块 > 匿名代码块 > 构造方法
1 public class Person { 2 // 输出顺序:2 赋初值~ 3 { //代码块(匿名代码块) 4 System.out.println("匿名代码块"); 5 } 6 // 输出顺序:1 , static 只执行一次~ 7 static{ //静态代码块 8 System.out.println("静态代码块"); 9 } 10 // 输出顺序:3 11 public Person() { 12 System.out.println("构造方法"); 13 } 14 public static void main(String[] args) { 15 Person person1 = new Person(); 16 System.out.println("============================="); 17 Person person2 = new Person(); 18 } 19 }
抽象类
-
abstract 修饰符可以用来修饰方法,也可以修饰类。
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
-
抽象类,不能使用 new 关键字类创建对象,它是用来让子类继承的。
-
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类继承的。
-
子类继承抽象类,就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
-
继承了抽象类的所有方法(包括抽象方法)的子类都必须要实现它的方法,除非子类也是抽象类。
-
Java中,类是单继承的,接口可以多继承。
-
抽象类特点:
-
不能new这个抽象类,只能靠子类去实现它:约束!
-
抽象类中可以写普通的方法
-
抽象方法必须在抽象类中
-
-
抽象类存在构造器吗?
-
抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。
在继承了抽象类的子类中通过super()或super(参数列表)调用抽象类中的构造方法。
接口
-
声明接口的关键词是 interface;
-
接口的作用:
-
约束
-
定义一些方法,让不同的人实现
-
接口中,所有定义的方法,其实都是抽象的 public abstract
-
public static final ~~ 属性其实都默认为常量,如 public static final int age;
-
接口不能被实例化,接口中没有构造方法
-
implements 可以实现多个接口;
-
必须要重写接口中的方法
-
内部类
-
内部类就是在一个类的内部再定义一个类。比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
-
内部类可以直接访问外部类方法和属性,不需要创建外部类的对象
-
一个Java类中可以有多个class类,但是只能有一个 public class 类
- 类型:成员内部类、静态内部类、局部内部类、匿名内部类
异常机制
-
检查性异常:最具代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
-
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略
-
Java把异常当做对象来处理,并定义一个基类 java.lang.Throwable 作为所有异常的超类。
在 Java API 中已经定义了许多异常类,这些异常类分为两大类,错误 ERROR 和 异常 Exception
Exception:
ERROR:
-
错误 ERROR :错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译时也检查不到的。
2.
异常处理机制
-
异常处理五个关键字:try、catch、finally、throw、throws
-
快捷键 : Ctrl + ALT + T ---> 选择代码块进行包裹代码(如:if/else , try/catch......)
自定义异常
实际应用中的经验总结