自学Java如何从入门到精通
https://www.zhihu.com/question/25255189?sort=created
一、准备知识
Java语言发展
特点:
简单: 针对C++简化了很多
跨平台:
Java平台体系:
Java SE Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java Platform,Enterprise Edition(Java EE)提供基础。 Java ME 这个版本以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端 Java 应用程序。 Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web 2.0 应用程序。 JavaEE Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。
注释
单行注释
// 单行注释. 这一行内容会被jvm运行的时候过滤掉.
多行注释
/*
多行注释
一次注释多行代码
*/
文档注释
/**
* 文档注释
* 后期可以使用javadoc命令生成api文档
*/
JDK安装和IDEA的安装
https://book.apeland.cn/details/81/
Java文件的编译和运行
在cmd命令行中运行:
- 首先进入文件所在目录
- 编译:
javac Hello.java
默认cmd命令行使用的ANSI编码,中文会乱码报错- 生成一个
Hello.class
文件
- 生成一个
- 运行:
java Hello
我们使用编译器IEDA写的代码都是utf8编码的,直接使用
java Hello.java
编译会报“编码GBK的不可映射字符” 的错误,有两种解决办法 1)在notepad++中全选剪切代码,改变编码为ANSI,然后粘贴保存 2)javac -encoding utf8 Hello.java
,指定编译.java文件的编码方法,但后面文件输出中有中文在cmd中显示还是会乱码,该方案适用于打印输入无中文的情况
在IDEA中编译运行:
注意: IDEA编写的代码第一行是包的依赖,如 package com.xjt.demo01; 如果使用命令行的方式运行会发现报错,找不到或无法加载主类 这时需要注释掉第一行
二、Java数据类型
1、基本数据类型
- 整数
- byte 字节. 1个字节, 范围: -128~127
- short 短整数. 2个byte, 范围: -32768~32767
- int 整数. 4个byte, 范围: -21亿~21亿-1
- long 长整数. 8个byte
- 浮点数
- float 单精度浮点数 精度低
- double 双精度浮点数 精度高
- 字符
- char 字符类型 表示单个字符. 2个字节byte
- 必须使用单引号,字符串使用的是双引号
- char类型可以存放数字,比如char c = 20037; 实际上是Unicode码中汉字 ‘久’
布尔
byte -> short -> int -> long -> float -> double
为什么long排在float前面?因为整数是有数据量的范围的,而小数是没有的,很简单的例子, 0~1有多少个小数? 无数个.
从小数据类型向大数据类型转化是安全的 ->直接转化
byte a = 10;
int b = a;
System.out.println(b); // 10
long c = b;
System.out.println(c); // 10
我们可以看到非常方便直接用就可以了,但是如果是大的数据类型向小数据类型转化的话,就需要强制类型转换(俗称强转).
int a = 100;
short b = (short) a;
System.out.println(b); // 100
强制类型转换的语法: (转换之后的数据类型) 变量
小结: 小 -> 大 : 自动转换
大 -> 小 : 强制类型转换
1.2 数据类型之间隐式转化
运算过程中,byte short char都会自动先转化为int
- 相同数据类型之间
相同数据类型之间计算, 得到的一定是这个数据类型
int + int = int - 不同数据类型之间
首先, 把小的数据类型自动转换成大的数据类型, 然后再进行计算, 得到的结果一定是大的数据类型.
int + long = long - 特殊的byte, short, char
在计算的时候,首先会转化成int类型然后再进行计算,这样是安全的。
byte + byte = int
结果至少是int结论:
- 在执行算数运算的时候,byte, short 会自动的转化成int然后再进行计算。
- 如果不同数据类型之间进行计算,比如int+long. 此时, 程序会自动的把int转化成long,然后再进行计算. 所以结果一定是大的数据类型
恶心人的题:
short s1 = 1;
short s2 = s1 + 1; //s1 + 1 会先将s1转化为int,相加之后得到的是int 在赋值给short会报错
System.out.println(s2);
解析: 此时第2行代码一定会报错,因为s1是short类型,而short类型计算的时候会自动转换成int进行计算,并且,所有的数字默认都可以看做是int类型,默认的小数都可以看做是double类型,所以第二行计算的结果应该是int类型,把int类型的数据赋值给short类型的变量一定会报错的,此处必须要进行强制类型转换。 short s2 = (short) (s1 + 1);
2、引用数据类型
引用数据类型非常多,大致包括:
类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型
例如,String类型就是引用类型。
简单来说,所有的非基本数据类型都是引用数据类型。
1.存储位置不同:
基本变量类型:
- 在方法中定义的非全局基本数据类型变量的具体内容是存储在栈中的
引用变量类型:
- 只要是引用数据类型变量,其具体内容都是存放在堆中的,而栈中存放的是其具体内容所在内存的地址
ps:通过变量地址可以找到变量的具体内容,就如同通过房间号可以找到房间
2.传递方式
基本变量类型:
- 在方法中定义的非全局基本数据类型变量,调用时按数值传递
引用变量类型:
-
3、字符串
注意区别字符(单引号括起来的单个字符),被双引号引起来的内容都是字符串
String name = "周杰伦";
System.out.println(name); //周杰伦
字符串可以执行加法运算. 表示字符串拼接.
String a = "你好";
String b = "赛利亚";
System.out.println(a+b); //你好赛利亚
System.out.println(1+a); //1你好
当出现非字符串和字符串进行相加的时候. 首先把非字符串自动转化成字符串然后再执行拼接操作
System.out.println("1" + 1); # 11
转义字符: 具有特定含义的字符串
\n : 换行
\t : 制表符
. : .
\’ : ‘
\”: “System.out.println(“你好啊, 我叫\n周润发”); //换行
你好啊, 我叫
周润发
System.out.println("玛丽说: \"她喜欢你\""); // 玛丽说: "她喜欢你"
System.out.println("hello\t world"); // hello world
4、数组
数组: 具有相同数据类型的集合。int数组装一堆int,String数组装一堆String。
数组对数据类型是非常敏感的, 所以在声明数组的时候, 就需要给定数组的数据类型。
Java的数组有几个特点: 数组所有元素初始化为默认值,整型都是
0
,浮点型是0.0
,布尔型是false
;- 数组一旦创建后,大小就不可改变。
要访问数组中的某一个元素,需要使用索引。数组索引从0
开始,例如,5个元素的数组,索引范围是0
~4
。
可以修改数组中的某一个元素,使用赋值语句,例如,ns[1] = 79;
。
可以用数组变量.length
获取数组长度
基本用法
创建数组
String[] arr = new String[10]; // 创建一个10个格子的数组. 有10个位置装数据. 都是空的
String[] games = {"LOL", "DNF", "绝地求生", "男友4"};
String[] smallGames = new String[]{"CS", "红色警戒", "war3", "扫雷"};
int[] ns = new int[5]; //长度为5的整型数组
int[] ns = {1,2,3,4,5,6,7,8}; //长度不定的整型数组
第一种, 只开辟内存空间,但不放入数据。
第二种和第三种, 开辟4个空间, 并放入数据数组的使用
String[] arr = new String[5];
arr[0] = "红色警戒";
arr[1] = "war3";
arr[2] = "CS";
arr[3] = "英雄联盟";
arr[4] = "男友5";
// arr[5] = "扫雷和埋雷"; // 报错. 数组下标越界 ArrayIndexOutOfBoundsException
System.out.println(arr[0]); //红色警戒
System.out.println(arr[1]); //war3
System.out.println(arr[2]); //CS
System.out.println(arr[3]); //英雄联盟
System.out.println(arr[4]); //男友5
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException 要我说几遍. 没有第5个
数组的遍历
1.取索引的方式遍历数组
String[] arr = {"CS", "红色警戒", "war3", "扫雷"};
for(int i = 0; i < arr.length; i++){ //数组.length就是数组的长度
System.out.println(arr[i]);
}
2.
forEach
循环,直接迭代数组的每个元素int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
注意:在
for (int n : ns)
循环中,变量n
直接拿到ns
数组的元素,而不是索引。
显然for each
循环更加简洁。但是,for each
循环无法拿到数组的索引,因此,到底用哪一种for
循环,取决于我们的需要。打印数组内容
直接打印数组变量,得到的是数组在JVM中的引用地址:
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(ns); // 类似 [I@7852e922
这并没有什么意义,因为我们希望打印的数组的元素内容。因此,使用for each
循环来打印它:
int[] ns = { 1, 1, 2, 3, 5, 8 };
for (int n : ns) {
System.out.print(n + ", ");
}
使用for each
循环打印也很麻烦。幸好Java标准库提供了Arrays.toString()
,可以快速打印数组内容:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(Arrays.toString(ns)); //[1, 1, 2, 3, 5, 8]
}
}
多维数组
较常见的多维数组就是二维数组,定义一个二维数组如下:
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(ns[0]); //[I@10f87f48
int[] arr0 = ns[0]; //arr0和ns[0] 指向相同的内存区域
System.out.println(arr0); //[I@10f87f48
ns[0][1]=20;
System.out.println(Arrays.deepToString(ns)); //[[1, 20, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
System.out.println(Arrays.toString(arr0)); //[1, 20, 3, 4]
System.out.println(arr0.length); // 4
命令行参数
Java程序的入口是main
方法,而main
方法可以接受一个命令行参数,它是一个String[]
数组。这个命令行参数由JVM接收用户输入并传给main
方法:
public class Main {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}
我们可以利用接收到的命令行参数,根据不同的参数执行不同的代码。例如,实现一个-version
参数,打印程序版本号:
public class Main {
public static void main(String[] args) {
for (String arg : args) {
if ("-version".equals(arg)) {
System.out.println("v 1.0");
break;
}
}
}
}
上面这个程序必须在命令行执行,我们先编译它,会生成一个Main.class文件:
$ javac Main.java
然后,执行的时候,给它传递一个-version
参数:
$ java Main -version
v 1.0
这样,程序就可以根据传入的命令行参数,作出不同的响应。
小结:
命令行参数类型是String[]
数组,保存在args中,可以通过索引或遍历取值;
注意:package com.xjt.demo01; IDEA编辑时第一行是引用的包名,如果使用命令行执行时需要注释掉,否则找不到该类。
- 练习:
1.倒序打印数组每个元素
int[] ns = { 1, 4, 9, 16, 25 };
// 倒序打印数组元素:
for (int i=ns.length-1;i>=0;i--) {
System.out.println(ns[i]);
}
2.数组冒泡排序
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
for (int i=0;i<ns.length-1;i++){
for (int j=i+1;j<ns.length;j++){
if(ns[i]>ns[j]){
int a = ns[i];
ns[i] = ns[j];
ns[j] = a;
}
}
}
System.out.println(Arrays.toString(ns)); //[8, 12, 18, 28, 36, 50, 65, 73, 89, 96]
3.判断一个数是否是质数
质数: 只能被1和自身整除的数
System.out.println("请输入一个数:");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
boolean flag=true;
for (int i=2;i<num;i++){
if(num%i == 0){
flag=false;
break;
}
}
if(flag){
System.out.println("num="+num+",是质数");
}else{
System.out.println("num="+num+",不是质数");
}
4.数组中最大数
int[] arr = {1,55,2,17, 8,12,5};
int max = arr[0];
for (int i=1;i<arr.length;i++){
if(max<arr[i]){
max = arr[i];
}
}
System.out.println("数组arr中最大数为:"+max);
4.计算数组平方和
int[] arr = {1,55,2,17, 8,12,5};
long sum=0;
for (int item:arr){
sum+=item*item;
}
System.out.println("数组arr各项的平方和="+sum)
5.鸡兔同笼问题:用户输入脚数目 判断鸡兔的所有可能情况
Scanner sc = new Scanner(System.in);
System.out.println("请输入鸡兔脚数目:");
int foots = sc.nextInt();
for (int i=0;i<=foots/2;i++){
//有i只鸡
if((foots-2*i)%4 == 0){
System.out.println("鸡有"+i+"只,兔子有"+((foots-2*i)/4)+"只");
}
}
//请输入鸡兔脚数目:
//10
//鸡有1只,兔子有2只
//鸡有3只,兔子有1只
//鸡有5只,兔子有0只
6.水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身(例如:1^3 + 5^3+ 3^3 = 153).
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个三位数:");
int num = sc.nextInt();
if(num<100 || num >999){
System.out.println("该数不是水仙花数");
}
int baiwei = num/100;
int shiwei = num%100/10;
int gewei = num%10;
if(baiwei*baiwei*baiwei+shiwei*shiwei*shiwei+gewei*gewei*gewei == num){
System.out.println("该数是水仙花数");
}
7.命令行执行.java文件
package com.xjt.demo01;
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) {
if(args.length == 0 || args[0].equals("-h")){
System.out.println(Arrays.toString(args));
}else if (args[0].equals("-g")){
System.out.println("goodbey,");
for (String s:args
) {
System.out.println(" "+s);
}
System.out.println("!");
}
}
}
javac Demo01.java
java Demo01 -h xiong taop
三、条件和循环控制语句
1.if语句
语法3(多分支语句): if(条件1){ 语句块1 } else if (条件2){ 语句块2 } else if(条件3){ 语句块3 }…. else { else语句 } 执行流程: 判断条件1是否成立. 如果成立. 执行语句块1, 否则, 如果条件2成立, 执行语句2, 否则, 如果条件3成立, 执行语句3……如果所有条件都不成立. 执行else语句.
// 输入考试分数
Scanner sc = new Scanner(System.in);
int score = sc.nextInt();
if(score >= 100){
System.out.println("优秀");
} else if (score >= 80 ){
System.out.println("良好");
} else if (score >= 70 ){
System.out.println("中等");
} else if (score >= 60 ){
System.out.println("及格");
} else {
System.out.println("不及格");
}
2.while语句
int a = 0;
while (a < 10){
System.out.println("还我钱");
a = a + 1 ;
}
3.switch语句
示例:
输入月份, 进行判断. 如果是1,2,3月. 输出第一季度, 如果是4,5,6输出第二季度. 以此类推输出第三季度和第四季度
Scanner sc = new Scanner(System.in);
int month = sc.nextInt();
switch (month){
case 1:
case 2:
case 3:
System.out.println("第一季度");
break;
case 4:
case 5:
case 6:
System.out.println("第二季度");
break;
case 7:
case 8:
case 9:
System.out.println("第三节度");
break;
case 10:
case 11:
case 12:
System.out.println("第四季度");
break;
default:
System.out.println("您输入的月份有问题. ");
break;
}
注意: break表示跳出switch. 如果不写break则会发生case穿透现象 case穿透: 如果有一个case匹配成功, 则后面的case不会继续判断而是直接执行case中的语句
4.for语句
语法: for(语句1; 语句2; 语句3){ 循环体 } 解释: 语句1: 一般初始化我们的循环变量 语句2: 条件判断, 是否继续循环 语句3: 一般做循环变量的改变 执行流程: 首先执行语句1, 然后判断语句2是否成立,如果成立(true)执行循环体,如果不成立(false),停止循环(for循环结束),最后执行语句3,改变循环变量。然后继续判断语句2是否成立. 如果成立, 继续执行循环体 再执行语句3….以此类推,直到语句2为假循环跳出。
案例: 从0数到9
for(int i = 0; i < 10; i++ ){
System.out.println(i);
}
特殊的for循环
for(int i = 0; i < 10; i--){
System.out.println(i);
}
由于i—的存在. i越来越小. i<10恒成立. 所以次循环将会永远执行下去. 像这样的循环, 被称为死循环
另类的死循环
for(;;){
System.out.println("傻x");
}
for each语法
这是for循环的增强语法,能直接取出数组/集合中的每一项,缺点是 不能获得下标
for(类型 变量:可迭代对象){ 循环体 }
5.do-while语句
while循环是先判断条件,条件满足继续执行循环体,
do-while是先执行循环体,再判断条件是否满足,满足则进入下一次循环即执行循环体,判断条件。。。
do { // 循环体 } while (条件判断);
int i = 0 ;
do{
System.out.println(i);
i++;
} while (i < 10);
6.break和continue
break:终止循环
for (int i = 0 ; i < 10; i++){
if(i == 7){
break;
}
System.out.println(i);
}
//0 1 2 3 4 5 6
continue:终止当前的循环,进入下一个循环
for (int i = 0 ; i < 10; i++){
if(i == 7){
continue; // 我不喜欢7
}
System.out.println(i);
}
//0 1 2 3 4 5 6 8 9
四、函数
1.定义方法
语法:
//static:普通的静态方法 void:该方法没有return返回值或返回值为空 public static void 方法名(){ 方法体 } 方法名() // 这里是调用方法
public static void yue() {
System.out.println("1. 拿出手机");
System.out.println("2. 打开陌陌");
System.out.println("3. 找一个美眉");
System.out.println("4. 谈谈人生理想");
System.out.println("5. 约出来看个电影");
}
public static void count100(){
for(int i = 1; i <= 100; i++){
System.out.println(i);
}
}
public static void main(String[] args) {
yue(); // 调用上面定义好的方法
System.out.println("爽啊...约完之后数100个数之后继续约");
count100();
yue(); // 调用上面定义好的方法
}
带参数的方法定义:
public static void 方法名(参数){ 方法体 } 方法名(参数)
参数两类:
- 在方法声明的位置的括号里写的内容被称为形参. 形式上. 你在调用该方法的时候需要给一个数据. 那接收数据的话, 咱么说过, 变量是用来保存数据的. 所以, 所谓的形参, 实际上就是声明一个变量
- 在方法被调用的地方给方法传递的具体的信息, 这个叫实参.
形参就是一个变量声明就可以了. 表示可以接受xxx类型的数据
实参必须是具体的数据, 可以是变量, 也可以直接是值.
public class Func_test {
public static void yue(String tools,String doWhat) {
System.out.println("1. 拿出手机");
System.out.println("2. 打开"+tools);
System.out.println("3. 找一个美眉");
System.out.println("4. 谈谈人生理想");
System.out.println("5. 约出来"+doWhat);
}
public static void count100(){
for(int i = 1; i <= 100; i++){
System.out.println(i);
}
}
public static void main(String[] args) {
yue("陌陌","看个电影"); // 调用上面定义好的方法
System.out.println("爽啊...约完之后数100个数之后继续约");
count100();
yue("soul","聚餐"); // 调用上面定义好的方法
}
}
接收不定参数:
//接收不定参数的整数相加
public static int addToSum(int a,int b,int... args){
int sum = a+b;
for (int item:args){
sum+=item;
}
return sum;
}
//主函数中调用
System.out.println(addToSum(1,2,3,4,5,6,7)); //28
方法的返回值:
带有返回值的方法:
public static 返回值类型 方法(参数){ 方法体 return xxx; }
public static int addTwoNum(int a,int b){
int sum = a+b;
return sum;
}
//主函数中调用
int ret = addTwoNum(1,2);
System.out.println(ret);
注意:
- 如果方法没有返回值, 那么在方法声明的地方必须要写void. 不可以空着.
- 如果方法有返回值. 那么在方法声明的地方必须写出方法的返回值类型.
- 如果方法有返回值. 在方法内部必须使用return语句来返回数据.
- 方法的返回值必须和返回值类型一致.
- return语句执行之后, 方法就结束了. 后面不能执行任何代码.
2.方法重载
重载:方法的名字相同,参数的个数或者类型不同。调用方法时传入不同的参数/类型 就会调用不同的执行方法;
我们发现, 在方法调用的时候. 程序会自动根据你给的参数类型和个数选择你要执行的方法.public class TestMethod6 {
public static void chi(String fan){
System.out.println(fan);
}
public static void chi(String fan, String cai){
System.out.println(fan);
System.out.println(cai);
}
public static void chi(String fan, String cai , String tiandian){
System.out.println(fan);
System.out.println(cai);
System.out.println(tiandian);
}
public static void main(String[] args) {
chi("大米饭");
chi("大米饭", "西红柿炒鸡蛋");
chi("大米饭", "尖椒土豆丝", "哈根达斯");
}
}
再看一个public class TestMethod7 {
public static int add(int a, int b){
System.out.println("两个int");
return a + b;
}
public static long add(int a, long b){
System.out.println("一个int, 一个long");
return a + b;
}
public static double add(int a, double b){
System.out.println("一个int, 一个double");
return a + b;
}
public static void main(String[] args) {
System.out.println(add(10, 20));
System.out.println(add(10, 20L));
System.out.println(add(10, 1.25));
}
}
五、输入输出
读取输入
前面已经看到,打印输出到’标准输出流’(即控制台窗口) 是一件非常容易的事情,只要
调用 System.out.println即可,然而读取”标准输人流” 就没有那么简单了,首先需要构建输入流Scanner in ```java Scanner in = new Scanner(System.in); System.out.println(“please input your name:”); String name = “”; if(in.hasNext()){ name = in.nextLine(); }
System.out.println(“please input your age:”); int age = in.nextInt();
System.out.println(“please input your score:”); double score = in.nextDouble();
double avg = score / 3.0; System.out.printf(“student:%s,age:%d,the score avg= %.2f”,name,age,avg);
<a name="cYMtQ"></a>
### ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1559629/1604281449721-facc6bf1-fa89-4cfb-bde6-83a55273d875.png#align=left&display=inline&height=330&margin=%5Bobject%20Object%5D&name=image.png&originHeight=330&originWidth=349&size=63600&status=done&style=none&width=349)
<a name="ywulc"></a>
### 格式化输出
[https://www.cnblogs.com/kunlbc/p/4518977.html](https://www.cnblogs.com/kunlbc/p/4518977.html)
```java
System.out.println(x);
System.out.printf("%,10.2f\r\n",x);
System.out.printf("%-,10.2f\r\n",x);
//输出
-3333.3333333333335
-3,333.33
-3,333.33