Java 编程思想

安装编程环境

jdk下载链接

Eclipse下载链接

一、对象导论

1、 抽象机制

1.1 抽象过程

所有编程语言都提供抽象机制。汇编语言是对机器指令的抽象,高级语言是对汇编语言的一种抽象。但它们抽象出来的东西还是基于计算机的一下东西,和问题没有直接关联。而面向对象是直接对问题的抽象。

对象:问题空间中的元素及其在空间中的表示。

面向对象的实质:程序可以通过添加新类型的对象使自身适应问题。OOP允许根据问题描述问题,而不是根据计算机描述问题。

Java语言五个基本特性:

  • 万物皆对象
  • 程序是对象的几何,通过发送消息告诉彼此要做的
  • 每个对象都有自己的由其他对象构成的存储
  • 每个对象都有其类型
  • 某一特定类型的所有对象都可以接受相同的信息

1.2 每个对象都有接口

接口对应了某一特定对象所能发出的请求。但是,在程序中,不许有实现这些请求的代码,于是就有了实现。每一个请求都有方法与之关联。

1.3 每个对象都提供服务

当试图开发或者理解一个程序设计时,最好的办法时把对象想象成“服务提供者”。

1.4 被隐藏的具体实现

将程序开发人员按角色分为类创建者客户端程序员是大有裨益的。类的创建者构建类,只把必须的部分暴露给客户端程序员。于是有了访问控制权限,它存在的原因:

  • 让客户端程序员无法触及它们不该触及的部分
  • 允许库的设计者改变类内部的工作环境而不必担心会影响到客户端程序员

1.5 复用和继承

组合:把多个类融入进一个类。它经常被是为"has a"(拥有)关系,比如汽车拥有引擎。继承没有组合的灵活性,因为编译器会给继承施加限制。在建立新类的时候,应该首先考虑组合。

1.6 继承

基本思想:如果我们可以以现有的类为基础,复制它,然后通过添加和修改这个副本创建新类就好多了。

导出类和基类有相同的类型。通过继承产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛。

1.6.1 是一个和像是一个的关系

类与导出类是is-a(是一个)关系。比如圆形是一个几何形状。

有时候必须在导出类型里添加新的接口(扩展接口)。这个新的类型可以替代基类,但是这种替代不完美,因为积累无法访问新添加的方法。这种情况我们描述为is-like-a(像是一个)关系。比如热力泵像是一个空调

1.7 多态

在处理层次结构时,经常把对象当成基类来看待。比如几何形状有很多子类。于是存在一个问题,如果要让几何形状绘制自己,编译器不知道要执行哪一段代码,对象自身会依据自己的类型执行恰当的代码

所以:编译器不肯产生传统意义上的函数调用,在OOP中,直到程序运行时才能确定代码的地址。

“你是一个Shape,我知道你可以erase()和draw()自己,那么去做吧,但是要注意细节的正确性”

1.8 单继承结构

所有的类都有一个超级基类:Object。它使得垃圾回收(内存管理)和异常处理变得容易很多

1.9 容器

开始的时候不知到会有多少个元素再里面,元素是可变的。如ArrayList,LinkedList

二、一切皆对象

2.1 用引用操纵对象

Java 建立的对象,拿到的是一个对象的一个引用,传递的也是对象的引用

2.2 存储

Java的所有对象存放到堆里

2.2.2 特例:基本类型

基本类型是存放于堆栈当中。boolean,char ,int ,double ....。基本类型具有包装容器类,比如

1
2
char c = 'x';
Character ch = new Character(c);

它们还可以互相转换

高精度数字

BigInteger和BigDecimal类。

2.2.3 Java的数组

创建数组对象的时候,创建的是一个引用数组,并且每个引用都会初始化成null。

2.7 第一个Java程序

1
2
3
4
5
public static void main(String[] args) {
System.getProperties().list(System.out);
System.out.println(System.getProperty("user.name"));
System.out.println(System.getProperty("java.library.path"));
}

2.8 注释和嵌入式文档

javadoc可以提取注释中的文档。它只能出现在/** */当中。可以使用独立文档标签@开头的东西。但是它会忽略掉private关键字的注释。

Java基础语法

foreach循环

可以用下面的for循环来遍历ArrayList

1
2
for (Integer i : l1) 
System.out.println(i);

字符串转换数字

1
2
3
String str = "1232";
int a = Integer.parseInt(str);
double b = Double.parseDouble(str);

随机数

1
2
3
4
Random rand = new Random(); // Random(24)里面可以写随机数种子
for (int j = 0; j < 20; j++) {
System.out.println(rand.nextInt(20));
}

转换成二进制数

1
2
int a = 7;
System.out.println(Integer.toBinaryString(a));

无符号位移>>>

1
2
-1 >>> 4 
1 >> 4 //两者不一样,如果正数,两者一样

带标签的流程控制

1
2
3
4
5
6
7
8
9
label1:
for (int i = 0; i < 999; i++) {
System.out.println("ggg");

for (int j = 0; j < 999; j++) {
System.out.println("hhh");
break label1; // break 2 层循环
}
}

### 值传递和引用传递

Java中的参数传参都是值传递,也就是复制一个副本传进去。如果是数组,对象的引用,就是复制一个引用传进去

1
2
3
4
5
6
7
8
static void swap(int[] arr) {
int a = arr[0];
arr[0] = arr[1];
arr[1] = a;
arr = new int[]{3,44};
}
//in: arr = {1, 3}
//out: arr = {3, 1}

由于值传递,不能写swap函数修改2个变量,但是却可以交换数组的值。

上面的函数不能从新new一个数组,但是却可以修改里面的值,因为引用被复制了,可以通过计算修改对应地址的值,却不能重新定义它。

控制台输入输出

Scanner输入

1
2
3
4
5
Scanner scan = new Scanner(System.in);
str = scan.nextLine();
a = scan.nextInt();
double doub = scan.nextDouble();
long ll = scan.nextLong();

println输出

1
System.out.println(str);

JAVA多态

类的类型

静态类型:定义时候左边的类型

动态类型:定义时候右边的类型

1
2
A a = new A(); 
A a2 = new B(); // A 是静态类型, B是动态类型

调用规则:

  • 首先找静态类型类里面的方法,匹配参数的静态类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static class A {
void f(A a) {
System.out.println("A.A");
}
void f(B b) {
System.out.println("A.B");
}
}
static class B extends A {
}
public static void main(String[] args) {
A a = new A(); A a2 = new B(); A a3 = new B();
a.f(a2); // A.A 动态类型也是A,所有运行A类的方法。 a2的动态类型B,静态类型A,匹配静态类型方法
a3.f(a2); // A.A 因为动态类型没有f方法,运行静态类型的方法
}
  • 然后运行的时候去动态类型里找对应的方法, 如果没有则运行静态类型的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static class A {
void f(A a) {
System.out.println("A.A");
}
void f(B b) {
System.out.println("A.B");
}
}
static class B extends A {
void f(A a) {
System.out.println("B.A");
}
void f(B b) {
System.out.println("B.B");
}
}
public static void main(String[] args) {
A a = new A(); A a2 = new B(); A a3 = new B();
a2.f(a3); // B.A 匹配静态类型中f(A)方法,然后去动态类型B里运行f(A)方法
}
  • 如果参数无法匹配会找父类的类型匹配。

  • 选择父类匹配时,调用中的静态类型会变为参数的类型

  • a的参数和方法一样用先在静态类型里找a=3,然后去动态类型里读取a=5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static class A {
int a = 3;
void f(A a) {
System.out.println("A.A");
System.out.println(a.a); // 这里左边的a是下面的b,它的参数去b里面找
}
// void f(B b) {
// System.out.println("A.B");
// }
}
static class B extends A {
int a = 5;
void f(A a) {
System.out.println("B.A");
System.out.println(this.a);
}
void f(B b) {
System.out.println("B.B");
}
}
public static void main(String[] args) {
A a = new A(); A a2 = new B(); A a3 = new B();
B b = new B();
a2.f(b); // B.A 5 参数无法匹配,找父类的匹配A.f(A)。然后运行动态类型的B.f(A)
}

JAVA容器和实用类

ArrayList

1
2
3
4
5
6
7
8
9
10
11
import java.util.ArrayList;
import java.util.List; // 头文件

List<Integer> l1 = new ArrayList<>();
l1.add(1); l1.add(-2); l1.add(9); // 添加元素
l1.get(0); // 第0个元素,
l1.addAll(l2); //加所有元素
l1.size(); // 大小
l1.isEmpty(); // 是否空
l1.contains(2); // 判断是否含有x元素
l.remove(4); // 删除元素

HashMap

1
2
3
4
Map<Integer,Integer> map = new HashMap<>(), map2 = new HashMap<>();
map.put(key, value); // 添加
map.get(key); // 拿出元素
map.putAll(map2); // 添加所有

高精度BigInteger

1
2
3
4
5
6
7
8
BigInteger bga = new BigInteger("123");
a = bga.intValue(); // 转换int
bga.multiply(bga); // 乘法
bga.subtract(bga); // 减法
bga = bga.add(bga); // 加法
bga.divide(bga); // 除法
bga.mod(new BigInteger("3")); // 取余运算
bga.compareTo(new BigInteger("333")); // 比较

Iterator迭代器

迭代器需要实现2个方法:hasNext()和next()。JAVA的容器里都由对应的迭代器方法.iterator

1
2
List<Integer> l1 = new ArrayList<>();
Iterator<Integer> lter = l1.iterator();

递归使用迭代器

如果需要递归使用迭代器,可以用subiterator来定义一个子迭代器来实现递归的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public Iterator<File> iterator() {
return new Iterator<File>() {
int index = -1;
Iterator<File> subIterator;

@Override
public boolean hasNext() {
if (files == null || index == files.size()) {
return false;
}
return true;
}
@Override
public File next() {
if (index == -1) {
index++;
return Directory.this;
} else if (index == files.size()) {
throw new NoSuchElementException();
}
if (subIterator == null) {
subIterator = files.get(index).iterator();
}
File ret = subIterator.next();
if (!subIterator.hasNext()) {
subIterator = null;
index++;
}
return ret;
}
};
}

或者可以模拟栈来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Iterator<FSElement> iterator() {  return new FSIterator(this);  }

private class FSIterator implements Iterator<FSElement> {
private List<FSElement> toDo;

public FSIterator(FSElement root) {
toDo = new LinkedList<>();
toDo.add(root);
}

public boolean hasNext() {
return !toDo.isEmpty();
}

public FSElement next() {
if (toDo.isEmpty()) throw new NoSuchElementException();
FSElement next = toDo.remove(0);
if (next instanceof Folder) {
Folder f = (Folder) next;
for (FSElement subitem : f.getChildren()) toDo.add(subitem);
}
return next;
}
}

Stream

lamda表达式的使用条件

  • 接口中只有1个方法
  • 默认实现方法除外
  • Object类对应方法除外

满足上述条件的接口是函数式接口可以在前面加上 @FunctionalInterface 来检查

1
2
3
4
5
6
7
@FunctionalInterface
public interface MyRunnable extends Runnable {
public abstract boolean equals(Object obj);
default void add() {
// pass
}
}

lamda表达式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void say(myInterface myInterface) {
System.out.println(myInterface.sayHello("fyind", "hello world"));
}

public interface myInterface { // 只有一个函数的接口
String sayHello(String name, String message);
}

static String sayHello(String name, String message) {
return name + message;
}

String sayHello2(String name, String message) {
return name + message;
}

// 调用
say((n, m) -> n+m);
say((String n, String m) -> n+m); // 定义类型
say((n,m) -> { // 语句块
return n + m;
});
say(MyStream::sayHello); // 引用方法, 参数类型必须一模一样, 静态
say(new MyStream()::sayHello2); // 实例对象引用
say(String::concat); // 参数对象引用

myInterface m = String::concat; // 也等价下面的写法
say(m);

(n, m) 是声明对象, 然后-> 后面写方法的实现。关于调用也有很多不同的写法

流的基本知识

1
2
3
4
5
6
graph LR
id(数据源) ==生成流==> 过滤
subgraph pipeline
过滤-->排序 --> 采集
end
采集 ==生成结果 ==> id2(结果数据)

流的特性

1、不改变数据

2、不存储数据

3、不可重复使用

流的操作方式

1、中值操作 (惰性操作)

2、终值操作只会有一个(采集操作)

中值操作可以有多个,终值操作只能有一个

判断终值和中值

返回值是stream是中值操作,否则是终值操作

1
2
l1.stream().peek(a -> System.out.println(a)); // 这句话不会打印输出,因为没有终值操作不会执行中值操作
l1.stream().peek(a -> System.out.println(a)).collect(Collectors.toList()); // 这句话有终值操作,会打印除l1里的元素

流的操作顺序

按照数据进行拆分,一个一个排队执行,不是从左到右一起执行。是小数据一个一个流。

流的生成

1. 数组变成流

Stream.of(arr) 方法可以把一个数组变成流

Arrays.stream(arr) 也可以实现相同的功能

1
2
3
String[] arr = { "ma", "zhi", "chu", "is", "java", "developer", "family" };
Stream<String> stream = Stream.of(arr);
Arrays.stream(arr);
1
2
Stream.of("a", "b", "c").forEach(System.out::println); 
Stream.iterate(0, a -> a+1).limit(10).forEach(System.out::println); // 迭代器生产

流的操作

1、peek 遍历(例子见上)

2、collect收集(例子见上)

1
.collect(Collectors.toList());  // 把流收集在List里面

3、limit 限制流的长度

4、forEach 对于每个生产的数

5、fliter 过滤

6、sorted排序

元素本身继承Comparable接口,通常是默认从小到大排序,使用Comparator.reverseOrder()可以倒序

1
2
3
4
String[] stra = new String[]{"ttsd","abd", "dk", "ddss", "kdk"};

Stream.of(12,2,1,5,13,6).sorted().forEach(System.out::println);
Arrays.stream(stra).sorted(Comparator.reverseOrder()).forEach(System.out::println);

按类中某关键字排序,该关键子必须由Comparable属性

1
2
3
4
//升序
List<User> ascUsers = users.stream().sorted(Comparator.comparing(User :: getAge)).collect(Collectors.toList()); // 按age排序
//降序
List<User> descUsers = users.stream().sorted(Comparator.comparing(User :: getAge).reversed()).collect(Collectors.toList());

实现Comparable接口

1
2
3
4
5
.sorted((p1, p2) -> { 
if (p1.getDistance() > p2.getDistance()) return 1;
else if (p1.getDistance() < p2.getDistance()) return -1;
else return 0;
});

文件操作

文件操作主要是由Path和File两个类和其中的方法完成的

创建文件

1
2
Path pt = Path.of("test.class");
File f = pt.toFile();

常用函数

获得文件名

1
pt.getFileName()

判断一个路径是文件还是文件夹

1
2
3
Files.isDirectory(pth)  // 是否是文件夹
Files.isRegularFile(pth) // 是否是文件
Files.exists(pth) // 是否存在

读取文本文件中的内容

1
List<String> lines = Files.readAllLines(file);

遍历目录下所有文件

1
2
3
4
Stream<Path> st = Files.walk(directory);  // 递归访问所有目录和子目录
st.map(Path::getFileName).forEach(System.out::println);

Stream<Path> st = Files.list(directory); // 非递归,只访问当前目录文件

多线程

继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 定义
class myThread extends Thread {
public void run() {
// ...
}
}

// 使用
myThread th = new myThread();
th.start(); // 开始

try { // 等待结束
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

同步

同步函数

1
2
3
synchronized public void calc() {
// .. 需要同步的代码
}

同步代码块

1
2
3
synchronized(this) {
// ... 同步代码块
}

网络编程

客户端

创建客户端

1
2
3
4
5
6
7
8
9
10
// 类属性
private Socket socket;
private int port;

// 连接服务器
try {
socket = new Socket(server, port);
} catch (Exception e) {
return false;
}

服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
// 类属性
private ServerSocket server;
private int port;

// 构造器里
try {
server = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}

// 监听
Socket s = server.accept();

交互操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private DataInputStream dis;
private DataOutputStream dos;

private String read() throws IOException { // 读取数据
String tp = "";
tp = dis.readUTF();
return tp;
}

private void write(String msg) throws IOException { // 发送数据
dos.writeUTF(msg);
dos.flush();
}

// 使用前

dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());