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
- 在使用过程中积累即可!
推荐廖雪峰老师的教程
下节继续