82 12345
发新话题
打印

[开发] 《Java教程》廖雪峰

《Java教程》廖雪峰

  这是专门针对小白的零基础Java教程。

  

  为什么要学Java?

  因为Java是全球排名第一的编程语言,Java工程师也是市场需求最大的软件工程师,选择Java,就是选择了高薪。

  为什么Java应用最广泛?

  从互联网到企业平台,Java是应用最广泛的编程语言,原因在于:
  • Java是基于JVM虚拟机的跨平台语言,一次编写,到处运行;
  • Java程序易于编写,而且有内置垃圾收集,不必考虑内存管理;
  • Java虚拟机拥有工业级的稳定性和高度优化的性能,且经过了长时期的考验;
  • Java拥有最广泛的开源社区支持,各种高质量组件随时可用。


  Java语言常年霸占着三大市场:
  • 互联网和企业应用,这是Java EE的长期优势和市场地位;
  • 大数据平台,主要有Hadoop、Spark、Flink等,他们都是Java或Scala(一种运行于JVM的编程语言)开发的;
  • Android移动平台。


  这意味着Java拥有最广泛的就业市场。

  教程特色

  虽然是零基础Java教程,但是覆盖了从基础到高级的Java核心编程,从小白成长到架构师,实现硬实力高薪就业!

  还可以边学边练,而且可以在线练习!

  并且,时刻更新至最新版Java!目前教程版本是:Java 17!

  最重要的是:免费!

  不要犹豫了!现在开始学习Java,从入门到架构师!

  
免费内容:
学习:官网

TOP

Java程序基础-变量与数据类型

char使用单引号,而字符串使用双引号

整型包括:
byte 1个字节
short 2个字节
int 4个字节
long 8个字节

浮点型包括:
float 4个字节
double 8个字节

字符型包括:
char 2个字节

常量使用final修饰符

var关键字:仅仅是少写了变量类型
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

Java程序基础-整数运算

移位运算
左移位 <<
代码:
int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n << 1;  // 00000000 00000000 00000000 00001110 = 14
int b = n << 2;  // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
右移位 >>

无符号右移位 >>>
最高位符号位总是补0

位运算
与运算:
两个都为1,结果才为1

或运算:
有一个为1,结果就为1

非运算:
0和1互换

异或运算:
两个数不同,结果为1
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

Java程序基础-浮点数运算

浮点数的特点
常常无法精确表示

比较两个浮点数
先计算其差的绝对值,然后判断是否小于一个很小的数
代码:
// 比较x和y是否相等,先计算其差的绝对值:
double r = Math.abs(x - y);
// 再判断绝对值是否足够小:
if (r < 0.00001) {
    // 可以认为相等
} else {
    // 不相等
}
溢出
除数为0不会报错,但会返回特殊值

  • NaN = Not a Number
  • Infinity = 无穷大
  • -Infinity = 负无穷大


强制转型
转为整数时:

  • 丢弃小数:直接转(int)
  • 四舍五入:+0.5后转
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

Java程序基础-布尔运算

短路运算
false && x 结果总是false
true || y 结果总是true

三元运算符
b?x:y
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

Java程序基础-字符和字符串

字符类型
一个char保存一个Unicode字符:
代码:
char c1 = 'A';
char c2 = '中';
也可以用\u+Unicode编码来表示
代码:
// 注意是十六进制:
char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65
char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013
如果把字符直接赋值给int类型,会赋值字符对应的Unicode编码(十进制)

字符串类型
双引号括起来

字符串连接
用+号

多行字符串
用三个双引号括起来"""
代码:
String s = """ 
           SELECT * FROM
             users
           WHERE id > 100
           ORDER BY name DESC""";
不可变特性
字符串是不可变的,指向可以变,但字符串本身不会变
代码:
public class Main {
    public static void main(String[] args) {
        String s = "hello";
        String t = s;
        s = "world";
        System.out.println(t); // t是"hello"还是"world"?
    }
}
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

Java程序基础-数组类型

数组创建
int[] myarray = {1,2,3}

获取数组长度用 数组变量.length 来获取

和字符串一样的不可变性

  • 数组不可变,重新赋值仅仅更改了指向
  • 数组其中的元素不可变,重复赋值元素,只是更改了元素的指向
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

流程控制-输入与输出

输出

System.out.println()
System.out.print()
System.out.printf()

printf()用到的占位符,表示格式化输出:

  • %d - 整数
  • %x - 十六进制整数
  • %f - 浮点数
  • %e - 科学计数法表示的浮点数
  • %s - 字符串


输入
利用scanner读取System.in输入流,来读取输入
代码:
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 创建Scanner对象
        System.out.print("Input your name: "); // 打印提示
        String name = scanner.nextLine(); // 读取一行输入并获取字符串
        System.out.print("Input your age: "); // 打印提示
        int age = scanner.nextInt(); // 读取一行输入并获取整数
        System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
    }
}
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

流程控制-判断与循环

引用类型判断
当判断引用类型是否相等时,使用equals()方法:s1.equals(s2)
为了防止s1为null报NullPointerException错误,使用短路运算符
代码:
s1 != null && s1.equals(s2)
for循环灵活使用
可以缺少初始化语句,循环条件和每次循环更新语句,甚至三者都可以不写
代码:
// 不设置结束条件:
for (int i=0; ; i++) {
    ...
}

// 不设置结束条件和更新语句:
for (int i=0; ;) {
    ...
}

// 什么都不设置:
for (;;) {
    ...
}
foreach写法
for(v:array)
foreach只能拿到值,无法拿到索引
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

数组操作

遍历数组

  • for
  • foreach
  • Arrays.toString()

数组排序
冒泡排序思路:
1. 对比数组中每两个相邻元素的大小,如果第一个比第二个大,则互换位置
2. 对比数据中所有相邻元素,从头开始到最后一对,这样最后一个元素就是最大的
3. 再次对所有相邻元素进行对比,除了最后一个
4. 重复1-3

多维数组
多维数组打印可以用Arrays.deepToString()

命令行参数
命令行参数接受的是String[]数组
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-方法

方法
一个类通过定义方法,可以给外部暴露接口,同时保证内部逻辑的一致性(比如防止随意设置内部私有变量)
this变量
局部变量与字段重名,局部变量优先级更高,这时需要使用this
可变参数
使用类型...定义
可变参数相当于数组类型,但是比数组参数好的地方在于:

  • 不需要构造String[]数组
  • 避免传入null参数

参数绑定
基本类型参数传递,是值传递,双方各自修改不会影响对方。
引用类型参数传递,调用方和接收方的参数变量指向的是同一个对象(引用类型),双方任意一方对引用参数修改,都会影响对方(因为双方都是指向同一个对象的引用)
Java中除了整型(byte,short,int,long),浮点类型(float,double),字符型(char),逻辑型(boolean)四种基本类型,其他都是对象(引用类型)。
有个特例:

  • 传参传入String,更改外部变量outKtsee,不会影响已经传入的变量inKtsee
  • 传参传入String[],更改外部变量中的某个元素outKtseeGroup[0],已传入的变量的元素也会更改inKtseeGroup[0]

原因是,1在更改外部变量,更改了外部变量的指向地址,而传入变量的指向地址没变;2在更改外部变量的元素时,更改了外部变量元素的指向地址,但外部变量(数组)本身的指向地址没变,因此传入变量(数组)的指向地址和外部变量一致,因而传入变量的元素也会随着变更。
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-构造方法与方法重载

默认构造方法
类没有定义构造方法时,系统自动生成一个默认构造方法,没有参数也没有执行语句。
对类中字段的初始化,先初始化字段,然后再由构造方法对字段进行初始化。
多构造方法
可以根据构造方法的参数变量,位置和类型,自动区分构造方法
如果调用其他构造方法,使用this(...)
代码:
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name) {
        this(name, 18); // 调用另一个构造方法Person(String, int)
    }

    public Person() {
        this("Unnamed"); // 调用另一个构造方法Person(String)
    }
}
方法重载(overload)
方法名相同,参数不同(返回值类型相同)
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-继承

继承使用extends关键字
OOP术语中父类称为:超类(super class),父类(parent class),基类(base class)
OOP术语中子类称为:子类(sub class),扩展类(extended class)

任意类均默认继承自Object类,编译器会自动加上extends Object

子类的字段不可以与父类重名

protected
为了让子类访问父类的字段,使用protected修饰关键字

super
super关键字表示父类。
子类引用父类的字段,可以用super.filedName

任何类构造方法第一行必须调用父类的构造方法;如果没有,编译器会自动加上super();(注意这里无参数)
子类的构造方法是编译器自动生成的,而非继承父类的构造方法

阻止继承
final,阻止任何类继承
sealed,允许部分继承
代码:
public sealed class Shape permits Rect, Circle, Triangle {
    ...
}
这样只有Rect,Circle,Triangle三个子类可以从Shape类继承

向上转型(upcasting)
把子类类型转为父类类型的赋值(安全)
代码:
//假设Redmi是Mobile的子类
Mobile m = new Mobile();
Redmi hm = new Redmi();
//这是可以的,因为Redmi手机拥有了手机的所有功能,如打电话,发短信等
Mobile mm = new Redmi();
mm.call();
mm.sms();
多余的方法和字段会被丢弃

向下转型(downcasting)
把父类类型转为子类类型的赋值(需要先判断)
跟上面相反,但可能会报ClassCastException错,因为缺少的功能无法凭空变出来

使用instanceof操作符可以预先判断
代码:
Person p = new Student();
if (p instanceof Student) {
    // 只有判断成功才会向下转型:
    Student s = (Student) p; // 一定会成功
    // 其他逻辑
}
//Java14开始的新写法
if (p instanceof Student s) {
    // 其他逻辑
}
组合和继承
继承是is关系,组合是has关系
代码:
//App不应该从Mobile继承,因为它不是手机,但它是属于手机中的一个实例
class Redmi extends mobile{
   protected App wechat;
   protected String CPU;
}
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-多态

重载(overload) = 方法名相同,参数不同
覆写(override) = 方法名相同,参数相同,返回值也相同(一般子类覆写父类的方法)

如果一个子类覆写了父类的方法,实例化某个父类类型时,通过向上转型的实际类型是子类;实际运行时调用父类被覆写的方法时,实际调用的是运行时实际类型的方法
代码:
public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run(); // 应该打印Person.run还是Student.run?
    }
}

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}
多态Polymorphi:Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。

利用多态,可以在代码中只与父类的方法打交道,而新增新的类型,只需要通过实际传入新的子类,就可以调用新的处理逻辑。
允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。

覆写Object方法
Object定义了以下方法:

  • toString()
  • equals()
  • hashCode()

任意类可以对这些方法覆写

调用Super
子类调用父类中被覆写的方法,使用super调用,例如:super.hello()

final
对方法加final,该方法不允许被覆写
对类加final,该类不允许被继承
对类的实例字段加final,该字段在初始化后不允许修改
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-抽象类和接口

抽象类
代码:
abstract class Person {
    public abstract void run();
}
面向抽象编程
通过抽象类的类型,去引用具体的子类的实例
代码:
Person s = new Student();
Person t = new Teacher();
// 不关心Person变量的具体子类型:
s.run();
t.run();
// 同样不关心新的子类是如何实现run()方法的:
Person e = new Employee();
e.run();
尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。


  • 上层代码只定义规范
  • 不需要子类就可以实现业务逻辑(正常编译)
  • 具体的业务逻辑由子类实现,调用者不关心


接口
比抽象类还抽象的纯抽象接口(没有字段)
所有定义方法默认public abstract
需要使用implements关键字
代码:
interface Person {
    void run();
    String getName();
}
一个类只能继承自一个类,但可以实现多个interface

接口继承
一个接口可以继承自另一个接口
代码:
interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}
default方法
接口中将方法加上default关键字,可以定义为default方法
default的方法不强制所有实现类去覆写(实现),只在需要的类中覆写(实现)
default的方法不能访问字段
代码:
interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-静态字段

实例字段在每个实例中都有自己的独立“空间”(互不干扰),而静态字段只有一个共享“空间”,所有实例会共享该静态字段。

静态字段可以看作是类的字段(而不是实例的字段),因此最好使用类命.静态字段的方式来访问
代码:
Person ming = new Person("Xiao Ming", 12);
ming.number = 88; //不推荐
Person.number = 99; //推荐
静态方法
调用静态方法不需要实例变量(实例化对象),直接通过类名调用
代码:
public class Main {
    public static void main(String[] args) {
        Person.setNumber(99);
        System.out.println(Person.number);
    }
}

class Person {
    public static int number;

    public static void setNumber(int value) {
        number = value;
    }
}
静态方法和静态变量一样,属于class,而不属于实例,因此静态方法内部不能使用this关键字,也无法访问实例字段,但可以访问静态字段
常用:

  • Arrays.sort()
  • Math.random()

包括main()也是静态方法

接口的静态字段
接口interface是纯抽象类,可以定义静态字段,且必须为final类型
代码:
public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}
因为接口字段只能是public static final,可以简写
代码:
public interface Person {
    // 编译器会自动加上public statc final:
    int MALE = 1;
    int FEMALE = 2;
}
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-包

包目录结构
代码:
package_sample
├─ src
│  ├─ hong
│  │  └─ Person.java
│  ├─ ming
│  │  └─ Person.java
│  └─ mr
│     └─ jun
│        └─ Arrays.java
└─ bin
   ├─ hong
   │  └─ Person.class
   ├─ ming
   │  └─ Person.class
   └─ mr
      └─ jun
         └─ Arrays.class
编译包需要在src目录下执行
代码:
javac -d ../bin ming/Person.java hong/Person.java mr/jun/Arrays.java
包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。

包作用域
同一包内可以访问protected和default(不加public,protected,private修饰字段)

import
代码:
import com.ktsee.Arrays; //导入com.ktsee包的Arrays类
import com.ktsee.*; //导入com.ktsee包的所有class
import static com.ktsee.Tool.*; //导入com.ktsee包中Tool类的所有静态字段和静态方法
编译器会默认这样做:

  • 默认自动import当前package的其他class
  • 默认自动import java.lang.*
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-作用域

public
class和interface可以被其他任何类访问
filed和method可以被其他包的类访问

private
filed和method无法被其他类访问,但可以被嵌套类(nested class)访问

protected
filed和method可以被继承的子类(或者子类的子类,或者其他包中继承的子类)访问,也可以被同一个包中的其他类访问

package
没有public,private修饰的class,以及没有public,protected,private修饰的字段
class,filed,method可以被同一个包内的其他类访问

局部变量
使用局部变量时,应该尽可能把局部变量的作用域缩小,尽可能延后声明局部变量。

final

  • 用final修饰class可以阻止被继承
  • 用final修饰method可以阻止被子类覆写
  • 用final修饰field可以阻止被重新赋值
  • 用final修饰局部变量可以阻止被重新赋值
代码:
package abc;

public class Hello {
    protected void hi(final int t) {
        t = 1; // error!
    }
}
最佳实践
尽量少使用public,减少暴漏对外的字段和方法
方法定义为package,可以和测试类放置在一起
一个.java文件只能包含一个public类(以及多个非public类),如果有public类,文件名必须和public类名相同
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-内部类(嵌套类)

Inner Class
嵌套类不能单独实例化,必须依附于一个已经存在的外部类
代码:
Outer outer = new Outer("Nested");
Outer.Inner inner = outer.new Inner(); // 调用Outer实例的new来实例化一个Inner
inner.hello();
内部类使用this访问自己,使用Outer.this访问嵌套的外部类
同时可以访问嵌套的外部类的private字段和方法

Anonymous Class
匿名类可以来自接口
代码:
Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};
也可以继承自普通类,可以添加静态代码来初始化数据
代码:
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<String, String> map3 = new HashMap<>() {
            {
                put("A", "1");
                put("B", "2");
            }
        };
        System.out.println(map3.get("A"));
    }
}
Static Nested Class
不在依附于已存在的外部类,可以单独存在
代码:
Outer.StaticNested sn = new Outer.StaticNested();
不可以引用Outer.this,但可以访问Outer类的static字段和方法
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

面向对象基础-classpath和jar

classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。

假设classpath是
代码:
.;C:\work\project1\bin;C:\share
当JVM加载com.ktsee.Hello类时,依次查找:

  • <当前目录>\com\ktsee\Hello.class
  • C:\work\project1\bin\com\ktsee\Hello.class
  • C:\share\com\ktsee\Hello.class


启动JVM可以通过-classpath参数传入(不推荐设置为系统环境变量)
代码:
java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello
简写
代码:
java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello
如果不传-cp参数,默认classpath是当前目录(即.)
不需要把Java核心库写入classpath

jar包
jar包是把bin目录下的内容(而非bin目录本身)打包成zip,修改后缀为jar即可
代码:
java -cp ./hello.jar abc.xyz.Hello
jar包中的/META-INF/MANIFEST.MF文件,可以指定Main-Class和其他信息
这样就可以无需指定jar包中的类名
代码:
java -jar hello.jar
流浪了那么多年,终于发现,这里才是我唯一的家。我只想回到这个对自己是那样熟悉和那样亲切的环境里,在和自己极为相似的人群里停留下来,才能够安心地去生活,安心地去爱与被爱。

TOP

 82 12345
发新话题