Java基礎
這裏主要介紹學習大數據過程中的Java基礎知識,歡迎大家共同交流。
環境
- Java的運行環境和機制如圖所示
- JRE(Java Runtime Environment) Java運行時環境
- JDK(Java Development Kit) Java開發工具包
需要在電腦上安裝Java SE,下載地址,我的電腦是Windows系統,安裝後將bin
目錄放入系統環境變量,使用cmd工具輸入java
或java -version
命令查看是否輸出相關信息 - JDK版本
- Java SE:Standard Edition,標準版
- Java EE:Enterprise Edition,企業版
- Java ME:Micro Edition,移動版
- 編譯器使用IntelliJ IDEA,就不要再使用eclipse了。下載安裝破解可以在網上查找資源
java程序
基礎部分
- java程序的基本結構如上圖所示,一個基本的java程序由類、方法、成員變量組成
- 和所有語言類似,java中的變量、數據類型、整數浮點數運算、布爾運算就不再贅述
如有疑問可以查看教程
包裝類型
- 我們常見的
int
float
變量不能視作對象(類),但是可以通過封裝成對應的類並實例化實現,也叫作包裝類型,JDK爲每種基本類型都創建了對應的包裝類型:
- 相互轉化
int a = 10;
Integer n = Integer.valueOf(a);
Integer n1 = Integer.valueOf("100");
int b = n.intValue();
int c = Integer.parseInt("100");
String str = n.toString();
Integer n = 99; // 自動裝箱
int i = n; // 自動拆箱 (這些都會影響效率,java是強類型語言)
Integer prop = Integer.getInteger(“cpus”); // 從系統環境中讀取環境變量
String
- 在java中,字符串屬於引用類型,Java 提供了 String 類來創建和操作字符串
public class StringDemo {
public static void main(String[] args) {// 字符串要使用雙引號
String s = "Hello"; // 字符串屬於引用類型,將引用類型賦給變量時:
String t = s; // 變量t和s同時指向字符串Hello,而不是t重新開闢內存
s = "World"; // 變量s指向 World,t仍指向 Hello
System.out.println(s); // World
System.out.println(t); // Hello 引用類型類似指針,不產生任何副本
}
} // 可以理解爲:普通變量是持有某個值(可以重寫),字符串變量是指向某個值(可以重定向)
字符串的相關方法和原理可以查看:引文
常見的有substring()
replace()
等
- StringBuilder類
package Hello;
public class hello {
public static void main(String[] args) {
String name = "Roy";
StringBuilder str = new StringBuilder();// 可變對象,可以高效拼接字符串(少佔內存)
str.append("Hello!").append(name).append("你最帥");// 支持鏈式操作(返回this)
System.out.println(str);
}
}
Array
- 數組變量使用“類型[ ]”標識
int[] arr = new int[5]; // 數組初始化
arr[0] = 1; // 數組是統一類型的數據集合
arr[1] = 2; // 可以通過索引訪問
arr[2] = 3; // 一旦創建大小不可改變
arr[3] = 4;
arr[4] = 5;
int[] arr1 = {1,2,3,4,5}; // 也可以使用這種方式創建並賦值
arr1 = new int[]{1,2,3}; // 數組也是引用類型,arr1重新指向了新的對象
System.out.println(arr1.length); // 打印數組長度
System.out.println(arr1); // 直接打印數組變量在JVM中的地址[I@6acbcfc0
System.out.println(Arrays.toString(arr1)); // 快速輸出數組內容
int[][] arr2 = { // 二維數組
{1,2,3,4,5},
{3,4,5,6,7},
{5,6,7,8,9}
};
System.out.println(Arrays.deepToString(arr2)); // 打印多維數組
注:java是強類型語言,每個變量前面必須限定類型
- 輸入輸出
import java.util.Scanner;
Scanner scanner = new Scanner(System.in);
System.out.println("Input your name:");
String name = scanner.nextLine(); // 輸入字符類型
System.out.println("Input your age: ");
int age = scanner.nextInt(); // 輸入int型
System.out.println(name+", you already "+age+"years old");
double d = 3.14159;
System.out.print(d); // 輸出不換行
System.out.printf("d: %.2f\n", d); // d: 3.14 格式化輸出
System.out.printf("d: %5.2f\n", d); // d: 3.14
- 條件判斷
public class conditionDemo {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "World";
if (s1 == s2) {
System.out.println("引用類型使用‘==’表示指向同一個對象");
}else if (s1.equals(s2)) {
System.out.println("判斷內容相等需要使用equals()方法");
}
else if (s1 != null && s1.equals(s2)) {
System.out.println("當變量爲null時equals()方法會報錯,所以使用短路運算符");
}else {
System.out.println("也可以將字符串對象放在前面");
}
double d = 1 - 9.0/10;
System.out.println(d); // 浮點運算存在誤差
if (Math.abs(d - 0.1) < 0.0001) { // 需要使用絕對值函數判斷
System.out.println("d is 0.1");
}else {
System.out.println("d is not 0.1");
}
}
}
- 循環
public class foreachDemo {
public static void main(String[] args) {
int[] num = {1,2,3,4,5};
for (int n : num){ // 也叫作增強型for循環,可以循環List/Map,因此循環是無序的
System.out.println(n);
}
}
} // continue 可以結束本次循環,進行下一次循環
- 命令行參數
public class ArgsDemo { // 命令行參數是 String[]
public static void main(String[] args) { // 類似於python中的*args和**kwargs
System.out.println("Number of args: "+ args.length);
for (String arg : args) {
if (arg.equals("-version")) {
System.out.println(arg);
}
}
}
}
// 命令行參數由JVM傳遞給main方法
// 使用快捷鍵[Alt+F12]即可在IDEA中調出命令行,輸入:java ArgsDemo.java -version -s -t "Hello" 查看輸出
// javac 文件名.java 可以將java文件編譯成.class文件
數據封裝
- 公有方法和私有方法,通過方法訪問成員變量
- 構造方法
- 方法重載
package Hello;
// 一切皆對象Object
public class Person { // 數據封裝
public String name;
public int age;
public Person(String name, int age) { // 構造方法:可以在實例化時初始化成員變量的值
this.name = name;
this.age = age;
}
public Person() { // 多個構造方法,實例化時可以根據參數的數量和位置自動選擇合適的構造方法
this("Roy",22); // 通過this進行實例化
}
public void setName(String name){ // 外部對象一般通過public修飾的方法訪問成員變量
this.name = name;
}
// 方法重載:功能相同的函數使用相同的名字
public void setName() { // 主要依靠參數類型和數量區分
System.out.println("這是setName方法的重載");
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
private int calcBirth(int age) {
return 2020 - age;
}
public int getBirth(int age){ // 內部方法可以調用私有方法
return calcBirth(age);
}
}
package Hello;
public class Main {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name + person.age);// Roy22
person.setAge(18);
person.setName("Roy");
System.out.println("Your name is " + person.getName());
// 公有方法調用私有方法
System.out.println("Your birthday is " + person.getBirth(23));
}
}
繼承
- 繼承是一種代碼複用的方式
- 一個類只能有一個父類,所有的類均繼承自Object類,形成一個繼承樹:
Object——Person——Girl & Boy
package Hello;
// 一切皆對象Object
public class Person /* extends Object */{
public String name;
public int age;
protected int speed; // protected 把變量和方法都控制在繼承樹內部
public Person(String name, int age) { // 構造方法:可以在實例化時初始化成員變量的值
this.name = name;
this.age = age;
}
public Person() { // 多個構造方法,實例化時可以根據參數的數量和位置自動選擇合適的構造方法
this("Roy",22); // 通過this進行實例化
System.out.println("自動調用父類的無參數構造器");
}
public void run() {
System.out.println("衝鴨!");
}
}
package Hello;
public class Girl extends Person {
String name;
int age;
public Girl(String name, int age) { // 構造方法
// 會自動調用父類的無參數構造方法,相當於super()
// 如果父類沒有無參的構造方法,必須調用父類的有參構造方法!
this.name = name;
this.age = age;
}
public void run() { // 繼承並重寫父類的方法
super.run(); // 複用了父類的代碼,提高效率
System.out.println("女孩向前衝!");
}
}
package Hello;
public class Boy extends Person {
String name;
int age;
public Boy(String name,int age) {
super("father",88); // 調用父類中有參數的構造方法
this.name = name;
this.age = age;
}
public void run() { // 繼承並重寫父類的方法
super.run(); // super關鍵字調用父類的成員
System.out.println(super.speed); // 子類不能調用父類的private變量,但可以使用protected變量
System.out.println("男孩向前衝!");
}
}
package Hello;
public class Main {
public static void main(String[] args) {
Person person = new Person("Roy",18);
Boy boy = new Boy("John",16);
Girl girl = new Girl("Diana",16);
boy.run();
girl.run();
}
}
/* 輸出:
父類的無參數構造器
衝鴨!
0
男孩向前衝!
衝鴨!
女孩向前衝! */
- 注意區分繼承和組合的關係,繼承是
is
,擁有相同特性的類之間的從屬關係;而組合是has
,可以通過在類中實例化其對象實現 - 繼承往往和多態相聯繫,多態有三要素:1.要有繼承關係 2.要有子類重寫父類成員方法 3.要有父類數據類型引用至子類
轉型
package Hello;
public class Main {
public static void main(String[] args) {
// 轉型
Person ps = boy; // 向上轉型(子類變成父類)
ps.run(); // 衝鴨!
Boy by = (Boy) person; // 向下轉型(父類變成子類),必須強制
// 由於子類不一定實現了父類的所有方法,所以會拋出異常
by.run(); // ClassCastException
}
}
抽象類
- 定義了抽象方法的類就是抽象類,必須使用
abstract
修飾 - 抽象類是爲了更加靈活地實現多態特性(子類可以控制父類的方法)
- 抽象方法相當於定義了子類必須實現(重寫)的接口規範
package Hello;
public abstract class CalcArea { // 抽象類
public abstract double area(); // 抽象方法,沒有任何實現的代碼
public double perimeter() { // 可以定義非抽象方法
return 0;
}
}
package Hello;
public class RectArea extends CalcArea{
private final double width; // final修飾成員變量,必須要賦初始值,且只能初始化一次
private final double height;// 所以必須使用有參構造方法賦值
// final修飾類的時候,表示這個類不能被繼承,類中的成員方法都會被隱式的指定爲final方法
// final修飾的方法不能被重寫
public RectArea(double width,double height) {
this.width = width;
this.height = height;
}
@Override
public double area() { // 實現父類的抽象方法
return this.height * this.width;
}
}
package Hello;
public class CircleArea extends CalcArea{
public double redius;
public CircleArea(double redius) {
this.redius = redius;
}
@Override
public double area() {
return Math.PI * redius * redius;
}
}
package Hello;
public class Main {
public static void main(String[] args) {
CalcArea Circle = new CircleArea(5); // 調用的是子類的方法,但實例化爲抽象類即可(因爲實現了所有父類的方法,可以向上轉型)
CalcArea Rect = new RectArea(5,5); // 我們只關心是否實現了抽象類的方法
System.out.println(Circle.area()); // 具體的實現完全由不同子類承擔,功能各異
System.out.println(Rect.area()); // 更加靈活的實現了多態的特性
}
}
接口
- 接口定義了純抽象的規範,使用
interface
關鍵字修飾 - 一個類可以實現多個接口,使用
implement
- 接口也是數據類型,適用於向上向下轉型
- 接口不能定義實例字段(即只能定義方法)
- 接口可以定義default方法,實現接口的類可以不用實現default方法
package Hello;
public interface InterfaceDemo {
double area(); // 接口方法
default double perimeter(){ // default方法,執行接口的類不用實現
return 0;
}
}
package Hello
public class CircleArea implements InterfaceDemo{ // 實現接口
public double redius;
public CircleArea(double redius) {
this.redius = redius;
}
@Override
public double area() {
return Math.PI * redius * redius;
}
}
- 接口可以繼承接口實現接口的擴展
package Hello;
public interface InterfaceDemo1 extends InterfaceDemo{
int speed();
}
public class CircleArea implements InterfaceDemo1{ // 實現接口
public double redius;
public CircleArea(double redius) {
this.redius = redius;
}
@Override
public double area() {
return Math.PI * redius * redius;
}
@Override
public int speed() {
return 0;
}
}
- 接口的層次代表了抽象的程度(可以先不理解)
- 接口與抽象類的區別:
靜態字段和方法
- 使用
static
修飾的字段和方法 - 普通字段在每個實例中都有自己的一個“獨立空間”
- 靜態字段只有一個“共享空間”,所有實例都共享該字段
package Hello;
public class Person {
static int number;
// 靜態方法
public static int getNumber() {
return ++number;
}
}
package Hello;
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
Person.number++; // 類名訪問靜態變量
System.out.println(Person.getNumber());
Person p2 = new Person();
Person.number++;
System.out.println(Person.getNumber());
Person p3 = new Person();
Person.number++;
System.out.println(Person.getNumber());
}
}
- 不推薦使用實例對象訪問靜態字段,因爲實例本身並沒有靜態字段,也是由編譯器轉化爲類名來訪問。推薦直接使用類名訪問!
- 調用靜態方法不需要實例對象
- 靜態方法通常用於工具類,例如:Arrays.sort() / Math.random()
包
- 我們自定義的類名可能與java中或其他人的類名衝突,這是就需要定義包
package
,上面的每一個小例子都在包Hello中 - java加載class並執行代碼時,總是使用類的完整類名,比如
java.util.Arays
,Hello.Person
,這樣就避免了重複命名引起衝突(類似於命名空間的概念),也因此,包沒有繼承關係! - 位於同一個包的類,可以訪問包作用域的字段和方法
- 前面使用了
public/protected/private
都代表了字段或方法不同的作用域,在一個包中不使用這些修飾的就默認在包的作用域
注:如果不能確定是否使用public方法,就不要使用,否則會暴露方法的
- java的核心類放置在
java.lang
包
- java文件的存放要按照包名的層次關係
classpath
- 是一個環境變量,指示如何搜索class
- 我們通常引入jar包避免大量的目錄和.class文件
- 創建jar包可以使用自帶的
jar
命令,或者使用構建工具如Maven - jar中可以包含一個特殊的 MANIFEST.MF 文件,用於指定Main-class等
- JDK自帶的class被打包在rt.jar中
JavaBean
-
命名規範:成員變量私有化,使用類的getter/setter方法訪問
enum常量
public enum Weekday {
SUN("星期天"), MON("星期一"), TUE("星期二"), WED("星期三"), THU("星期四"), FRI("星期五"), SAT("星期六");
private String chinese; // 定義私有變量
private Weekday(String str){// 定義構造方法
this.chinese = str;
}
public String toChinese(){
return this.chinese;
}
}
package Hello;
public class Main {
public static void main(String[] args) {
Weekday sun = Weekday.SUN;
System.out.println(sun.toChinese());// 星期天
System.out.println(sun.name()); // SUN (不推薦使用toString())
}
-
可以理解爲以類形式封裝的常量
常用工具類
- Math
- Random
- 在使用過程中積累即可!
推薦廖雪峯老師的教程
下節繼續