因为后面可能要接触
flink的东西,先快速入门java,像语法和oop特性 很多语言都是相通的~
注意下面第3点,JavaVirtualMachines/jdk1.8.0_131.jdk文件中可能看不到这个.jdk的东西,可以通过/usr/libexec/java_home -V查看java的所有版本jdk对应的位置,会有这个.jdk的路径。
# mac中查看Java的JDK安装路径
/usr/libexec/java_home -V
# mac安装多个版本的jdk后环境变量的设置和切换
# 1. jenv来管理java的JDK
brew install jenv
# 2. 设置环境
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(jenv init -)"' >> ~/.bash_profile
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
# 3. 添加jdk到jenv
jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home
jenv add /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home
jenv add /Library/Java/JavaVirtualMachines/jdk-13.0.1.jdk/Contents/Home
# 4. 选择使用相应版本的jdk
jenv shell 1.8 // 这里如果不行就重启一下命令行工具,新打开一个terminal也可以
# 5. 切换成功,查看java版本
java -version
首先新建项目,不用创建模板,然后打开项目中的src文件夹:

右击src选择new->Package:

得到如图:

在刚才新建的com.java.demo中右键new,java class,名字随便写个hello_andy:

在新建好的类中写一个main方法,和其他语言类似,java项目从这里开始执行:

最终的结果:

(1)在mac中找到jdk的位置
/usr/libexec/java_home -V
即可找到所有jdk的文件路径,如果查看当前默认jdk版本,可通过java -version(如果用jenv versions则是显示出所有的java版本);如果想在terminal上切换其他版本的jdk为默认jdk,(如从jdk 18切换到1.8版本)可通过jenv shell 1.8则是切换到1.8版本的jdk。
(2)可以利用jenv进行mac上的java多版本管理:
$ brew install jenv安装jenv后,可以通过jenv remove 1.8可以移除1.8版本的管理(注:可能有多个1.8版本,可以进一步通过jenv versions查看当前管理的所有java jdk版本)$ jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_351.jdk/Contents/Home/
参考:
(1)Mac 安装和使用 jenv 管理多版本 java
(2)Mac 12.5 安装和使用jenv管理多版本java
POM(Project Object Model):项目对象模型。
pom.xml文件包含了项目依赖的信息,当执行Maven命令(如mvn clean install)时,Maven会根据pom.xml文件中的依赖信息,自动下载对应的依赖包,并将它们添加到项目的类路径中,pom.xml文件后可以对其编辑,如添加项目依赖、插件配置和构建设置等信息:
pom.xml文件中添加spark的依赖信息,然后执行maven命令来自动下载和导入spark包,如添加:<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>2.4.6</version>
</dependency>
</dependencies>
参考:
(1)maven的官方文档。
(2)【Maven】pom.xml教程
一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。认识几个概念:

注意:
.java文件内不是只能写一个class,是可以有多个类的,但是只能有一个public的类,并且该类名要和文件名相同。public类。简单案例,循环,其中的打印System.out.print(),print()是一个方法,System是系统类,out是标准输出对象:
package com.java.demo;
public class loop_test {
public static void main(String[] args){
int x = 10;
while (x < 15){
System.out.print("value of x: " + x);
x++;
System.out.print("\n");
}
}
}
//value of x: 10
//value of x: 11
//value of x: 12
//value of x: 13
//value of x: 14
java基本语法:
MyFirstJavaClass 。public static void main(String[] args) 方法开始执行。Java修饰符:像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
Java 中主要有如下几种类型的变量
//java 的注释
public class HelloWorld {
/* 这是第一个Java程序
* 它将输出 Hello World
* 这是一个多行注释的示例
*/
public static void main(String[] args){
// 这是单行注释的示例
/* 这个也是单行注释的示例 */
System.out.println("Hello World");
}
}
六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型:byte、short、int、long、double、boolean、char。其中注意byte类型是8位,有符号的,以二进制补码表示的整数,即最小值是-128,最大值是127。
一个方法的定义:
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}

和C++语言不同的是,java可以定义finalize()方法来清除回收对象,关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用。Java 的内存回收可以由 JVM 来自动完成。如果你手动使用,则可以使用finalize方法:
package com.java.demo;
public class FinalizationDemo {
public static void main(String[] args){
Cake c1 = new Cake(1);
Cake c2 = new Cake(2);
Cake c3 = new Cake(3);
c2 = c3 = null;
// 调用java垃圾收集器
System.gc();
}
}
class Cake extends Object{
private int id;
public Cake(int id){
this.id = id;
System.out.println("Cake Object " + id + "is created");
}
protected void finalize() throws java.lang.Throwable{
super.finalize();
System.out.println("Cake Object " + id + "is disposed");
}
}

java Object是所有类的父类,如果没有明确继承一个父类,java会自动继承Object,即显示继承和隐式继承:
public class Test1 extends Object{ ...}
public class Test2{ ...}
一个类可以包含以下类型变量:
java中是通过new实例化对象,和其他oop一样,同时会调用构造方法初始化对象。
(1)栗子1:访问实例变量和调用成员方法:
package com.java.demo;
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public void setAge( int age ){
puppyAge = age;
}
public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}
public static void main(String[] args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}
//小狗的名字是 : tommy
//小狗的年龄为 : 2
//变量值 : 2
(2)import外部文件的类:import java.io.*;将会命令编译器载入 java_installation/java/io 路径下的所有类:
首先是员工类:
package com.java.demo;
import java.io.*;
public class Employee {
// public: name实例变量对子类可见
public String name;
int age;
String designation;
// private: 私有变量,仅在该类可见
private double salary;
// Employee 类的构造器
public Employee(String name) {
this.name = name;
}
// 设置age的值
public void empAge(int empAge) {
age = empAge;
}
/* 设置designation的值*/
public void empDesignation(String empDesig) {
designation = empDesig;
}
/* 设置salary的值*/
public void empSalary(double empSalary) {
salary = empSalary;
}
/* 打印信息 */
public void printEmployee() {
System.out.println("名字:" + name);
System.out.println("年龄:" + age);
System.out.println("职位:" + designation);
System.out.println("薪水:" + salary);
}
}
接着是main函数,同样是开头有句import java.io.*;找到我们上面的员工类:
package com.java.demo;
import java.io.*;
public class EmployeeTest{
public static void main(String[] args){
/* 使用构造器创建两个对象 */
Employee empOne = new Employee("RUNOOB1");
Employee empTwo = new Employee("RUNOOB2");
// 调用这两个对象的成员方法
empOne.empAge(26);
empOne.empDesignation("高级程序员");
empOne.empSalary(1000);
empOne.printEmployee();
empTwo.empAge(21);
empTwo.empDesignation("菜鸟程序员");
empTwo.empSalary(500);
empTwo.printEmployee();
}
}
extends申明继承。和其他oop语言类似,子类拥有父类非private的属性和方法,也可以用自己的方式实现父类的方法。
this,如果调用自身类的方法则用this.xxx_function,而python中调用自身类的方法是用self.xxx_function;super,但是C++没有super;virtual,当子类调用fun()时,会先在子类中找,找不到会报错。Java的抽象类和C++那边是差不多的:
public abstract class Employee,注意抽象类不能被实例化对象,需要被继承,子类重写方法。多态存在的三个必要条件:
Parent p = new Child();java中的多态:当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
java多态
package com.java.demo;
public class DuotaiTest {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
//父类引用指向子类对象
A a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(A a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
//父类animal
abstract class A {
abstract void eat();
}
class Cat extends A {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends A {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
结果为:
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
注意:@override表示子类的对父类方法的重写,是一种典型的标记式注解(只有编译器知道);如果子类重写的方法在父类中不存在,则编译代码时编译器会报错。
【虚函数】
java中其实没有虚函数概念,其普通函数相当于C++的虚函数,默认行为动态绑定(加上final关键字可以让某个函数变成非虚函数)。
略。
(1)访问控制修饰符:java有四种不同的访问权限:
public。使用对象:类、接口、变量、方法。private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)public : 对所有类可见。使用对象:类、接口、变量、方法protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。(2)非访问控制修饰符
static 修饰符,用来修饰类方法和类变量:
static变量。final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。abstract 修饰符,用来创建抽象类和抽象方法。synchronized 和 volatile 修饰符,主要用于线程的编程。这里举个static的栗子,可以看到实例化对象500个后,静态变量是独立于不同的对象的,最终静态变量numInstances累加到500的数值:
//静态变量和方法的小栗子
package com.java.demo;
public class StaticTest {
//静态变量
private static int numInstances = 0;
protected static int getCount(){
return numInstances;
}
private static void addInstance(){
numInstances++;
}
//main函数
public static void main(String[] args) {
System.out.println("Starting with" + StaticTest.getCount() + " instances");
for(int i = 0; i < 500; i++) {
//new StaticTest();
StaticTest temp = new StaticTest();
temp.addInstance();
}
System.out.println("Created " + StaticTest.getCount() + " instances");
}
}
// Starting with 0 instances
// Created 500 instances
import语句应该在package语句之后。java.lang打包基础的类,java.io包含输入输出功能的函数。参考:https://www.runoob.com/java/java-package.html
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
先浏览下图的Java集合框架图,Java 集合框架主要包括两种类型的容器:

所有的集合框架都包括(参照下图,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包):
ArrayList、LinkedList、HashSet、HashMap。
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。
package com.java.demo;
import java.util.ArrayList;
import java.util.Collections;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList<String> sites = new ArrayList<String>();
//如果是存储数字
ArrayList<Integer> myNumbers = new ArrayList<Integer>();
sites.add("Google");
sites.add("Weibo");
sites.add("AndyGuo");
sites.add("AAAA");
System.out.println(sites);
//访问第二个元素
System.out.println(sites.get(1));
//将下标为2的元素改为Wiki
sites.set(2, "Wiki");
System.out.println(sites);
//删除下标为1的元素
sites.remove(1);
System.out.println(sites);
//迭代数组列表
//for(int i = 0; i < sites.size(); i++){
// System.out.println(sites.get(i));
//}
//也可以用for-each来迭代元素
for (String i: sites){
System.out.println(i);
}
System.out.println("==========排序后:=========");
Collections.sort(sites);
for (String i: sites){
System.out.println(i);
}
}
}
链表,一种线性表,不按线性的顺序存储数据,如果经常增加or删除元素可以用该数据结构。

HashSet基于HashMap实现,允许有null值;
HashSet不是线程安全的:如果有多个线程尝试同事修改它,则最终结果是不去定的,即必须在多线程访问时显式同步对 HashSet 的并发访问。

package com.java.demo;
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
//创建hashmap对象
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
Sites.put(1, "AAAA");
Sites.put(2, "BBBB");
Sites.put(3, "CCCC");
Sites.put(4, "DDDD");
System.out.println(Sites);
//通过get(key)获得对应的value
System.out.println(Sites.get(3));
//如果只想获取key 可以使用keySet方法
//输出key 和 value
for(Integer i: Sites.keySet()) {
System.out.println("key:" + i + "; value:" + Sites.get(i));
}
//返回所有value值
for(String value: Sites.values()){
//输出每一个value
System.out.println(value + ", ");
}
//直接得到所有的value
System.out.println(Sites.values());
}
}
/*
{1=AAAA, 2=BBBB, 3=CCCC, 4=DDDD}
CCCC
key:1; value:AAAA
key:2; value:BBBB
key:3; value:CCCC
key:4; value:DDDD
AAAA,
BBBB,
CCCC,
DDDD,
[AAAA, BBBB, CCCC, DDDD]
*/
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
package com.java.demo;
import java.util.*;
public class MapTestt {
public static void main(String[] args) {
//也可以用第一种方法
//HashMap m1 = new HashMap();
HashMap<String, Integer> m1 = new HashMap<String, Integer>();
m1.put("andy", 1);
m1.put("bob", 2);
m1.put("ccc", 3);
System.out.print(m1);
}
}
package com.java.demo;
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorTest {
public static void main(String[] args) {
ArrayList<String> sites = new ArrayList<String>();
sites.add("AAAA");
sites.add("BBBB");
sites.add("CCCC");
sites.add("DDDD");
//获取迭代器
Iterator<String> it = sites.iterator();
System.out.println(it.next());
//逐个遍历集合的所有元素
while (it.hasNext()){
System.out.println(it.next());
}
}
}
略。
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
一个线程的生命周期:

一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。下图是一个描述输入流和输出流的类层次图。

JDK 提供的并发容器总结
ConcurrentHashMap
CopyOnWriteArrayList
ConcurrentLinkedQueue
BlockingQueue
ConcurrentSkipListMap
使用线程池的好处
Executor 框架
(重要)ThreadPoolExecutor 类简单介绍
(重要)ThreadPoolExecutor 使用示例
几种常见的线程池详解
ScheduledThreadPoolExecutor 详解
线程池大小确定
何谓悲观锁与乐观锁
乐观锁常见的两种实现方式
乐观锁的缺点
CAS与synchronized的使用情景
(1)Java内存区域
概述
运行时数据区域
HotSpot 虚拟机对象探秘
重点补充内容
(2)JVM垃圾回收
揭开 JVM 内存分配与回收的神秘面纱
对象已经死亡?
垃圾收集算法
垃圾收集器
(3)JDK 监控和故障处理工具
JDK 命令行工具
JDK 可视化分析工具
略,参考408系列。
[1] https://edu.csdn.net/job/javabe_01/java-4744df73a92b434593040c21501362c3
[2] runoob教程:https://www.runoob.com/java/java-character.html
[3] Spark之IDEA创建基于Scala语言的Spark Maven项目