日积月累 Python 有意思的一些小技巧

括号的区别

1
2
3
4
5
6
7
8
9
10
11
>>> a = [1,2,3]
>>> a
[1, 2, 3]
>>> type(a)
<type 'list'>
>>> a = (1,2,3)
>>> a
(1, 2, 3)
>>> type(a)
<type 'tuple'>
  • ( ) 表示 tuple,不可变类型

  • [ ] 表示 list,可变类型

列表推导式

1
2
3
4
5
>>> a = [1,2,3]
>>> a
[1, 2, 3]
>>> [ ea*2 for ea in a ]
[2, 4, 6]

元组没有元组推导式

交换

1
2
3
4
a = 12
b = 24
# exchange a b
a, b = b, a

输出序列以及倒序输出

1
2
3
4
5
6
print range(1,10)
print range(1,10)[::-1]
#output:
#[1, 2, 3, 4, 5, 6, 7, 8, 9]
#[9, 8, 7, 6, 5, 4, 3, 2, 1]

加 r 不转义

1
2
3
4
5
6
7
print "\\savc\nff"
print r"\\savc\nff"
#output:
#\savc
#ff
#\\savc\nff

格式化 对其输出 log 信息

1
2
3
4
5
6
print 'succeed'.center(20,'=')
print 'fail'.center(20,'=')
# output
# ======succeed=======
# ========fail========

center 表示居中的意思

单引号和双引号都可以表示字符串

1
2
print 'hello world'
print "hello world"

都是输出 hello world

这点要注意和java区分,因为java单引号表示字符char,双引号表示字符串string

pass的使用

如果想定义一个空函数,可以这样:

1
2
def staCorrect():
pass

如果把上面的pass去掉是会报错的。

程序员常见面试之 计算机网络 知识点小结

前言

悉心整理校正了计算机网络的面试常用知识点,图文并茂,通俗易懂,希望对大家有所帮助。

文章链接 http://www.alijava.com/network-interview/ ,分享请保留出处。

TCP/IP 参考模型

分层

每一层都是独立存在的,因此分配到各层的任务能够独立地执行,变更其中某层提供的方案时不影响其他层。

这也可以理解为一个松耦合的设计。

tcpip 模型

详解

网络层 主要是 ip 协议,可以通过 ip 找到某个机器,但是机器上有很多应用程序,每个程序都占用相应的端口,传输层 不同协议有不同的端口,通过 ip + 端口 就可以确定某个机器上的具体应用程序了。

Server A 和 Server B 直接传输数据,有封装和解封装的过程。

数据从A 的应用层往下走到物理层,会在每一层都会加上头部信息,进行封装,然后再发送到B。

然后 Server B 从 最下面的物理层往上 每一层进行解封装,最后到达应用层,得到数据。

  • 物理层:将电脑连起来,可以用光缆、电缆、双绞线、无线电波等方式

  • 链路层:数据包会带上发送地址和接收地址,也就是MAC地址

  • 网络层:网络地址帮助我们确定计算机所在的子网络,MAC地址则将数据包送到该子网络中的目标网卡。
    因此,从逻辑上可以推断,必定是先处理网络地址,然后再处理MAC地址。

  • 传输层:同一台主机上有许多程序都需要用到网络,端口可以确定表示这个数据包到底供哪个程序使用。
    传输层建立”端口到端口”的通信,”网络层”的功能是建立”主机到主机”的通信。
    (ftp 21,ssh 22,dns 53,http 80)

  • 应用层 我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容。
    如果想要使传输的数据有意义,则必须使用到应用层协议。

  

TCP 区别 UDP

TCP连接是由4个值来识别的: <源IP地址、源端口号、目的IP地址、目的端口号>

  • TCP是面向连接的、可靠的
    其实网络的不安全不稳定特性,无论多少次握手都不能保证连接的可靠性
    但TCP的三次握手在最低限度上(实际上也很大程度上保证了)保证了连接的可靠性
  • UDP 无连接的、不可靠
    UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,

 UDP 交给上层进行差错控制,可以代码对UDP的数据收发进行验证,比如发送方对每个数据包进行编号然后由接收方进行验证啊什么的

 即使是这样,UDP因为在底层协议的封装上没有采用类似TCP的“三次握手”而实现了TCP所无法达到的传输效率

  

IP 协议

A 类 B 类 C 类

IP的地址字段范围是0~127,但是由于全0和全1的地址用作特殊用途,实际可指派的第一个字段范围是1~126。

A类地址范围:0.0.0.1 —— 126.255.255.254, 默认网络掩码为 255.0.0.0

B类地址范围:128.0.0.1 —— 191.255.255.254,默认子网掩码为255.255.0.0

C类地址范围:192.0.0.1 —— 223.255.255.254,默认子网掩码为255.255.255.0

0 到 128 相差128,128 到192相差64,后面192到224相差32。

A 类地址, 0 开头, 128 个
B 类地址,10 开头,16,384 个
C 类地址,110 开头,2,097,152 个

特殊的地址

ip 地址= 网络号 + 主机号

网络号 主机号 用途
全为0 全为0 表示本主机,用作源地址,启动时用,获取了 IP 地址后不再使用
全为0 host-id 本地网络上主机号为 host-id 的主机,只作为源地址
全为1 全为1 有限广播(本地网络),只作目的地址,各路由器都不转发
net-id 全为1 定向广播(net-id标识的网络),只作目的地址
net-id 全为0 标识一个网络
127 任意 本地软件回送地址

B类中169.254.0.0到169.254.255.255是保留地址。如果你的IP地址是自动获取IP地址,而你在网络上又没有找到可用的DHCP服务器,这时你将会从169.254.0.0到169.254.255.255中临得获得一个IP地址。

私有地址

私有地址包括3组
A类:10.0.0.0~10.255.255.255
B类:172.16.0.0~172.31.255.255
C类:192.168.0.0~192.168.255.255

学校里的局域网,因为大概有几十万人,所以是10开头的局域网。

而我们买的无线路由器,也要设置局域网,一般为192开头的,比如192.168.0.1或者192.168.199.1(比如极路由就是这个地址)

我们建企业网(单位网络)时,一般是使用私有地址来分配内部主机,小企业使用C类的192.168.0.0网络,中型企业使用172.16.0.0网络,如果还不够用,还有10.0.0.0 网络。

子网划分

以C类网络为例,原有8位主机位,2的8次方即256个主机地址,默认子网掩码255.255.255.0。

借用1位主机位,产生2个子网,每个子网有126个主机地址;借用2位主机位,产生4个子网,每个子网有62个主机地址……

练习题

把子网掩码为255.255.0.0的网络40.15.0.0分为两个子网,假设第一个子网为40.15.0.0/17,则第二个子网为?

40.15.0.0/17 表示网段是40.15.0.0,子网掩码是17位。

子网掩码为:255.255.128.0,用二进制表示为:11111111 11111111 10000000 00000000 ,斜杠后面的17表示子网掩码前17位为1.

答案:

划分为两个子网的话子网掩码应该为255.255.128.0
第一个子网是40.15.0.0/17,可用地址范围是40.15.0.1~40.15.127.254
第二个子网是40.15.128.0/17,可用地址范围是40.15.128.1~40.15.255.254

解析:

掩码的前两个八位是255,第三个八位是128,有2的一次方个子网(因为第三个八位只有一个1,后面七位都是0),所以ip 地址第三个8位是0000,0000或者1000,0000,即0或者128。

因为第3个8位是1000,0000,可以区分开头第一个数是0还是1, 区分了两个子网。所以这两个子网是40.15.0.0和40.15.128.0

NAT穿透技术

NAT(Network Address Translation,网络地址转换)是一种网络地址翻译技术,将内部私有IP地址改变成可以在公网上使用的.

NAT

NAT三种实现方式

  • 静态地址转换:一个公网IP对应一个内部IP,一对一转换

  • 动态地址转换:N个公网IP对应M个内部Ip,不固定的一对一IP转换关系.同一时间,有M-N个主机无法联网.

  • 端口多路复用:对外只有一个公网IP,通过端口来区别不同内部IP主机的数据.

网关

  • 网关(gateway)能在不同协议间移动数据

  • 路由器(router)是在不同网络间移动数据
    相当于传统所说的IP网关(IP gateway)

三次握手

一些名词

  • seq (Sequence Number)序列号,这是为了连接以后传送数据用的
  • ack(Acknowledgment Number)确认序列号,是对收到的数据包的确认,值是等待接收的数据包的序列号。
  • SYN synchronous 同步信号
  • ACK Acknowledgement 应答信号, 当 ACK=1时候表示ack字段有意义
  • SYN 和 ACK 也表示 TCP 的标志位?

三次握手的步骤

三次握手

  1. 客户端 发起握手,目的端点是 服务端 的端点 post_server
    • 生成一个随机数作为它的初始化发送序号 x
    • 发出一个同步报文段,SYN=1,发送序号 seq=x
    • 并进入SYN_SEND状态,等待服务器确认
  2. 服务端监听到端口 post_server 上有连接请求,响应
    • 生成一个随机数作为它的初始发送序号 seq = y
    • 发出同步报文字段并对主机 A 端口1的连接请求进行确认,发送ack=x+1
    • 即发送 SYN+ACK 包,此时服务器进入SYN_RECV状态
  3. 主机 A
    • 发出对 服务端 端口 post_server 的确认,确认序号 ack=y+1,还有seq=x+1
    • 客户端和服务器进入ESTABLISHED状态,完成三次握手

为什么采用3次握手而不是2次握手?

  • 第一次握手 客户端发,服务端 知道 客户端 可以 发消息
  • 第二次握手 服务端收和发,客户端 知道 服务端 可以 接收消息 和 发消息
  • 第三次握手 客户端收和发,服务端 知道 客户端可以 接收消息 和 发消息

3 次是双向通信的最小值,也就是 SYN, SYN ACK, ACK ,两个发送、两个接收 ,其中第二次把接收和发送合在一起了

如果两次握手的话,客户端有可能因为网络阻塞等原因会发送多个请求报文,这时服务器就会建立连接,浪费掉许多服务器的资源。所以要增加第三次握手。

第3次失败会怎么办?

第三次失败,只有客户端处于成功状态(因为第2次服务器返回了ACK),服务器端没有接收到客户端的 ACK。

这要分几种情况讨论:

  • In other words, if the ACK is dropped but the next packet is not dropped, then everything is fine.
    也就是说客户端发出的 ACK 丢失了,发出的 下一个数据包 没有丢失,则服务端接收到下一个数据包(这个数据包里也会带上 ACK 信息),能够进入正常的 ESTABLISHED 状态

  • 如果服务端和客户端都没有数据发送,或者服务端想发送数据(但是发不了,因为没有收到客户端的 ACK),服务器都会有定时器发送第二步SYN+ACK数据包,如果客户端再次发送ACK成功,建立连接。

    如果一直不成功,服务器肯定会有超时设置,超时之后会给客户端发RTS报文,进入CLOSED状态,防止SYN洪泛攻击。

四次握手关闭连接

四次握手

(1)主机 A 关闭 A主机的 端口1 到 B主机的 端口2 的传输连接:

  • 应用程序通知 TCP 数据已经发送完毕时,关闭连接
  • TCP 向主机 B 发送一个带 FIN 附加标记的报文段(FIN 表示 finish),FIN=1,seq=x

(2)主机 B 响应:

  • 收到这个 FIN 报文段之后,并不立刻用 FIN 报文段回复主机 A,而是先向主机 A 发送一个确认序号 ,ACK=1,ack=x+1
  • 同时通知自己相应的应用程序,主机 A 方传输已经结束,对方要求关闭连接(先发送 ACK 的目的是为了防止这段时间内,主机 A 重传 FIN 报文段)

(3)主机 B 关闭 端口2到端口1的传输连接:

  • 应用程序告诉 TCP: 我要彻底地关闭连接
  • TCP 收到对最后数据的确认后,向主机 A 发送一个 FIN 报文段。FIN=1,seq=y,ACK=1,seq=x+1。

(4)主机 A 响应:

  • 收到这个 FIN 报文段之后,向主机 B 发送一个 ACK 表示连接彻底释放。ACK=1,ack=y+1
  • 主机B收到主机A的ACK报文段以后,就关闭连接;此时,主机A等待2MSL (Maximum Segment Lifetime)后依然没有收到回复,则证明主机 B 已正常关闭,那好,主机A也可以关闭连接了。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

TCP是全双工模式,关闭连接时,每个方向都必须单独进行关闭。

当 主机 B 收到主机 A 的FIN报文时,仅仅表示主机 A 不再发送数据了但是 A 还能接收数据。

如果主机 B 全部数据都已经发送给 A 了,B 可以立即close;也可以发送一些数据给 A 后,再发送 FIN 报文关闭连接。

因此, 主机 B 的 ACK 和 FIN 一般都会分开发送。

参考文章

Java 模拟 HTTP Get Post 请求实现论坛自动回帖

设计思路

最近想自动发帖回帖,拿某论坛试验了一下,发现可行,不过后续没有再使用,以免影响论坛正常运行。

本文链接 http://www.alijava.com/simulate-bbs/ 转载请注明出处

  1. 帖子链接的格式为
    http://bbs.***.***.**/forum.php?mod=viewthread&tid=774210
    最后面774210数字变化, 就可以得到不同的帖子

  2. 防止帖子发表会又被删了的情况, 进行判断帖子是否存在

  3. 递增后面的 id 数字, 对每个链接做回帖的 POST 请求

重难点

回帖需要用户登录信息

  • 一种是利用Cookie

  • 另一种是进行模拟登录

本文采用前者

判断 url 对应的帖子是否存在

有可能用户发了帖子,比如 url 为 http://bbs.***.***.**/forum.php?mod=viewthread&tid=774200

后来该帖子用户删除了或者被管理员删除了,虽然帖子不在了,但是该 tid=774200 还是存在的

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
33
34
35
36
public static boolean isExist(int id) {
String tmpPath = baseRefer + id;
URL url;
try {
url = new URL(tmpPath);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.addRequestProperty("Content-Type", "text/html; charset=UTF-8");
con.addRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36");
con.addRequestProperty("Referer", "http://t.dianping.com/register");
con.setRequestMethod("GET");
if (con.getResponseCode() == 200) {
InputStream inputStr = con.getInputStream();
String info = new String(StreamTool.read(inputStr), "UTF-8");
if (info.contains("抱歉,指定的主题不存在或已被删除或正在被审核")) {
System.out.println("id=" + id + "帖子存在或已被删除!");
return false;
}
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}

模拟发帖

代码比较简单,注意事项是找到自己的Cookie,赋给String yourCookeie

用post发送一个回帖,回帖信息在 mapData.put("message", "友情帮顶了")

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
private static final String baseRefer = "http://bbs.**.**.**/forum.php?mod=viewthread&tid=";
private static final String yourCookeie = "Q8qA_2132_saltkey=**; Q8qA_2132_lastvisit=****3699;";
public static void main(String[] args) {
int startId = 774210; // you need change
for (int i = 0; i < 100; i++) {
postMessage(startId);
startId++;
}
}
public static void postMessage(int id) {
if (!isExist(id)) {
return;
}
String tmpPath = baseRefer + id;
StringBuilder path = new StringBuilder(tmpPath);
Map<String, String> mapData = new LinkedHashMap<String, String>();
mapData.put("mod", "post");
mapData.put("action", "reply");
mapData.put("replysubmit", "yes");
mapData.put("infloat", "yes");
mapData.put("handlekey", "fastpost");
mapData.put("inajax", "1");
mapData.put("message", "友情帮顶了");
mapData.put("formhash", "86ec5d81");
try {
for (Map.Entry<String, String> mapEnt : mapData.entrySet()) {
path.append("&");
path.append(mapEnt.getKey() + "=");
path.append(URLEncoder.encode(mapEnt.getValue(), "UTF-8"));
}
URL url = new URL(path.toString());
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(path.length()));
con.setRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36");
con.setRequestProperty("Cookie", yourCookeie);
con.setDoOutput(true);
OutputStream outStr = con.getOutputStream();
outStr.write(path.toString().getBytes());
if (con.getResponseCode() == 200) {
InputStream inputStr = con.getInputStream();
String info = new String(StreamTool.read(inputStr), "UTF-8");
System.out.println("在id=" + id + "成功发帖!");
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

还有一个工具方法,将输入流转化为字节

1
2
3
4
5
6
7
8
9
10
11
12
13
class StreamTool {
public static byte[] read(InputStream inputStr) throws Exception {
ByteArrayOutputStream outStr = new ByteArrayOutputStream();
// TODO Auto-generated method stub
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStr.read(buffer)) != -1) {
outStr.write(buffer, 0, len);
}
inputStr.close();
return outStr.toByteArray();
}
}

效果图

www.alijava.com

Linux Shell Vim 常用命令、使用技巧总结

前言

本文总结了自己实际开发中的常用命令,不定时更新,方便自己和其他人查阅。

如有其他提高效率的使用技巧,欢迎留言。

本文地址 http://www.alijava.com/linux-basics/ 转载请注明出处

Linux 常用命令

基本

  • cd - 返回到上一次的工作目录

  • export LANG=en_US,export 对于环境变量的设置也能作用于子shell

  • ps -ef | grep nginx ,搜索,这儿用到了管道

  • passwd root 修改密码

  • top 监控CPU 内存等

  • $? 上一个命令执行的退出状态,成功为0,不成功为非0

  • hostname 获取 ip

  • alias vi="vim" 在/etc/profile (用户登录自运行配置文件)的末尾添加vi的别名设置

ls 常用参数

  • -a 列出所有(含隐藏文件等):
  • -ltr 按照时间升序
  • -lt 按照时间降序

  • 统计文件数目

    wc -l输出文件行数,也就是文件的个数

    可以通过 grep 过滤文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@** _posts]# ls -l
    total 156
    -rw-r--r-- 1 root root 13607 Aug 26 14:07 android-interview.md
    -rw-r--r-- 1 root root 16287 Aug 26 14:07 arrays-sort.md
    -rw-r--r-- 1 root root 2951 Aug 30 12:03 common-tools.md
    [root@** _posts]# ls | wc -l
    3
    [root@** _posts]# ls | grep ".md" | wc -l
    3

查找文件

  • locate
    locate 是基于内部系统表的, 速度快, 但是可能更新不即时
  • find . -name "*.sh" -or -name "*.c"
    find 即时查找, 速度相对较慢

端口占用情况

  • netstat -tnpl | grep 12
  • kill -9 42312 杀掉进程,-9 表示强制杀

sed 使用

  • sed '$d' datafile 除最后一行,其余行都被显示
  • sed '/my/d' datafile 除包含 my 的行,其余行都被显示
    这两个命令并不会真的删除文本里的某些行
  • sed -e 's/\.md$//'去掉后缀 .md
  • sed -e 's/^/http:\/\/www.alijava.com\//'加上网站前缀
  • sed 's/old/new/'old 替换为new

shell 常见用法

$# 表示接收到的参数个数
$1 表示第几个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
if [ $# -ne 4 ]
then
echo "usage: bash autofetch.sh <space> <windows/linux> <command> <number>"
exit -1
fi
which=$1
if [ $2 = "windows" ]
then
machine=WINDOWS
elif [ $2 = "linux" ]
then
machine=LINUX
fi

vim 常用命令

方向移动

  • h, j, k, l 分别代表向左、下、上、右移动

    在这些j前加一个数字,表示移动的倍数。例如,”10j“表示向下移动10行

  • ^ 移动光标到行首 0(数字0)也可以实现相同功能
    ^ 是移到第一个到行首的第一个非空白字符
    0 只是移动到行首,不区分空白或非空白

  • $ 移动光标到行尾

  • gg 回到文件开头 H 也可以回到文件最上面

  • G 到最下面

复制粘贴

  • dd 删除,前面加上数字表示删除多少行,比如100dd
  • dw 删除单词
  • d$ 删除到这行末尾的单词
  • yy 复制
  • p 粘贴
    它们都可以加上数字参数表示操作多少行

输入文字

  • i 当前文字位置插入 insert
  • a 当前文字的后面插入 append
  • o 下一行插入
  • w 光标到下一个单词的词首
  • e 移动光标到下一个单词的结尾

查找替换

  • /string 查找
  • n 查找下一个匹配的
  • :set nu 显示行号

保存

  • :wq 写入并退出
  • :q! 强制退出 不保存修改

在线学习

推荐一个在线学习vim网站 http://www.atool.org/vim.php

参考文章

http://easwy.com/blog/archives/advanced-vim-skills-basic-move-method/

Java抓取网络图片上传七牛云全纪录

前言

项目想把图片上传到七牛云存储上,笔者把七牛最简单的用法示范一下,方便初学者。

如果您还没有注册七牛,可以通过我的邀请链接注册 https://portal.qiniu.com/signup?code=3lpzf1unpyr0y,可以享受特有优惠以及免费空间。

http://www.alijava.com/qiniu-fetch/

比如上面的 beautiful girl 图片的 url 是
http://img.blog.csdn.net/20160119111734404

我们最终用代码上传到七牛图床后,七牛云存储的 url 为
http://7xq7ik.com1.z0.glb.clouddn.com/testimage

本文地址 http://www.alijava.com/qiniu-fetch/ 转载请注明出处

使用七牛

准备工作

  1. 注册一个七牛账户
    网址 https://portal.qiniu.com/signup?code=3lpzf1unpyr0y

  2. 登录七牛开发者自助平台,查看 Access Key 和 Secret Key (这两个 Key 代码会用到)

    http://www.alijava.com/qiniu-fetch/

下载七牛依赖 jar 包

新建空间

图片会上传到某个空间里,代码里会用到空间名,本项目空间名为 seenews

http://www.alijava.com/qiniu-fetch/

代码

抓取图片

我们的项目需求是抓取网络图片:

输入图片原始的的 url,然后利用代码抓取到七牛图床上,返回图片在七牛上的 url

首先定义变量存放你的 Key 和空间名。

1
2
3
public static final String ACCESS_KEY = "******"; // 你的access_key
public static final String SECRET_KEY = "******"; // 你的secret_key
public static final String BUCKET_NAME = "******"; // 你的secret_key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取到 Access Key 和 Secret Key 之后,您可以按照如下方式进行密钥配置
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
//获取空间管理器
BucketManager bucketManager = new BucketManager(auth);
try {
// 要求url可公网正常访问BucketManager.fetch(url, bucketName, key);
// @param url 网络上一个资源文件的URL
// @param bucketName 空间名称
// @param key 空间内文件的key[唯一的]
DefaultPutRet putret = bucketManager.fetch(originalUrl, BUCKET_NAME, "testimage");
System.out.println(putret.key);
System.out.println("succeed upload image");
} catch (QiniuException e1) {
e1.printStackTrace();
}

代码思路很简单,根据 key 获取到空间管理器,再指定图片的原始 url、空间名、图片的 key,就可以把图片上传到七牛上了

在七牛的空间内容管理部分可以看到图片已经上传好了,新的 url 为 http://7xq7ik.com1.z0.glb.clouddn.com/testimage

http://www.alijava.com/qiniu-fetch/

那么问题来了,如何获取图片新的 url 呢?总不能每次都通过浏览器访问七牛的网站,如何在代码里获取我们图片在七牛上的 url 呢?

域名+key 获取新的 url

还记得刚才的代码里我们给图片指定了 key 吗,只是图片在七牛空间里的唯一表示。

刚才我们指定的 key 为testimage,看新的图片 url 为http://7xq7ik.com1.z0.glb.clouddn.com/testimage,新url就是刚才的 key 结尾的。

前面一大串的字符是七牛给我们的默认域名,你也可以自定义域名。

http://www.alijava.com/qiniu-fetch/

那么代码思路也很简单,定义一个变量存放空间的域名,然后域名拼接图片的 key 就得到图片上传后的 url。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static final String BUCKET_HOST_NAME = "7xq7ik.com1.z0.glb.clouddn.com";
public String uploadByUrl(String originalUrl) {
//...
String imageKey = "testimage";
DefaultPutRet putret = bucketManager.fetch(originalUrl, BUCKET_NAME, imageKey);
newUrl = BUCKET_HOST_NAME + "/" + imageKey;
System.out.println(newUrl);
System.out.println("succeed upload image");
} catch (QiniuException e1) {
e1.printStackTrace();
}
return null;
}

输出信息

1
2
7xq7ik.com1.z0.glb.clouddn.com/testimage
succeed upload image

邀请注册

如果您还没有注册七牛,可以通过我的邀请链接注册 https://portal.qiniu.com/signup?code=3lpzf1unpyr0y,体验用户有一些免费空间。

参考文章

七牛操作工具类(Java版)

七牛关于JAVA SDK的各种Demo

Java 基于七牛云实现随机图库 API

需求

项目源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

本文链接 http://www.alijava.com/random-image/ 转载请注明出处

在实现APP 过程中,有些文章没有图片,想随机获取一张图片作为封面。

网上找到一个 API,访问 https://unsplash.it/300/200/?random 可以获取一张随机产生的图片。

后面笔者为了方便国内网络访问,把图片转移到了七牛云,共享给大家。

方案一

把图片的 src 设为 https://unsplash.it/300/200/?random ,图片是随机的了,但是两张随机图片是一模一样的,参加下面的代码和效果图。

1
2
3
<img src="https://unsplash.it/300/200/?random" alt="blog.csdn.net/never_cxb" title="">
<img src="https://unsplash.it/300/200/?random" alt="blog.csdn.net/never_cxb" title="">

你可以刷新本博客文章,两张图会随机变化,但是两张图依然保持一样。

不知道这是不是和浏览器缓存有关系,请大神赐教。

blog.csdn.net/never_cxb

blog.csdn.net/never_cxb

方案二

该网站也提供了根据 id 来获取图片,使用方法在 url 后面加上 ?image参数。

https://unsplash.it/200/200?image=100 的图片内容为:

blog.csdn.net/never_cxb

笔者使用下面的代码来随机产生 id,从而获取随机图片

1
2
Random randrom = new Random(47);
String url = "https://unsplash.it/640/427/?image=" +randrom.nextInt(1000);

但是测试发现,一小部分id对应的图片不存在,比如 https://unsplash.it/200/300?image=86https://unsplash.it/200/300?image=105 等等。

抓取图片

笔者把该 API 提供的图片抓取到了七牛云上,保证图片的 id 是连续的

可以通过我的邀请链接 https://portal.qiniu.com/signup?code=3lpzf1unpyr0y 注册七牛,享受优惠及免费空间。

代码

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class UploadRandomImage {
private static final String RANDOM_URL = "https://unsplash.it/640/427/?image=";
private static final String ACCESS_KEY = "*****"; // 你的access_key
private static final String SECRET_KEY = "*****"; // 你的secret_key
private static final String BUCKET_NAME = "*****"; // 你的空间名称
public static void main(String[] args) throws InterruptedException, IOException {
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
// 获取空间管理
BucketManager bucketManager = new BucketManager(auth);
int key = 0;
for (int i = 0; i < 1025; i++) {
// 如果 i 对应的图片存在,上传七牛
if (exists(i)) {
bucketManager.fetch(RANDOM_URL + i, BUCKET_NAME, key + "");
// 只有上传了七牛,key 才+1,保证七牛的 key 连续
key++;
// sleep一段时间,免得对网站负载过大
Thread.sleep(1000 * 10 + new Random().nextInt(1000 * 10));
} else {
System.out.println(i + "不存在");
}
}
System.out.println(key + "最终图片数目");
}
/**
* 判断地址对于的图片是否存在
* @param id
* @return
*/
public static boolean exists(int id) {
try {
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(RANDOM_URL + id).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

总结

可以访问 http://7xr4g8.com1.z0.glb.clouddn.com/671 获取图片,后面数字671是图片编号

blog.csdn.net/never_cxb

目前有效图标编号的是 0 到 964,可以通过随机产生 id 来获取随机图片

1
2
Random randrom = new Random(47);
String url = "http://7xr4g8.com1.z0.glb.clouddn.com/" +randrom.nextInt(964+1);

项目源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

Python 找出多个连续的空闲端口

项目需求

代码检测某个端口是否被占用,从而找出多个连续空闲的端口,由于需要 Windows、Linux 都能运行,所以选择了 Python,而不是 shell。

实现方案

Python 的 socket 模块

思路是 try 尝试连接某个端口,如果能连接上,表示端口被占用,否则端口空闲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def isInuse(ipList, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
flag=True
for ip in ipList:
try:
s.connect((ip, int(port)))
s.shutdown(2)
print '%d is inuse' % port
flag=True
break
except:
print '%d is free' % port
flag=False
return flag

try 模块中 如果 s.connect((ip, int(port))) 如果能成功, 说明端口被占用.

否则, connect 不成功, 会进到except 中, 说明端口不被占用.

存在问题

1
2
3
4
netstat -tnpl
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 11563/java
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 11563/java

源 ip 、源端口 + 目的 ip、目的端口 可以确定一条 tcp 连接。

本来以为 s.connect((ip, int(port))) ,只需要检测 "127.0.0.1","0.0.0.0"

后来发现还有可能是局域网ip 如 10.170.70.87 等等

可以通过这个方法根据远程 hostname 获得其 ip

1
2
3
def getLocalIp():
localIP = socket.gethostbyname(socket.gethostname())
return localIP

代码

从某个端口 port 开始,进行检测,port + 1、port + 2 … ,如果连续的空闲端口数目达到需求的 N,则 port ~ port + N 为需求的端口。

如果中途某个 port + L 端口被占用,那么从新的端口 port + L + 1 开始寻找。

本代码只针对 ipList = ("127.0.0.1","0.0.0.0",getLocalIp()) 这3个 ip 进行 connect

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import sys
import os
import socket
def isInuse(ipList, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
flag=True
for ip in ipList:
try:
s.connect((ip, int(port)))
s.shutdown(2)
print '%d is inuse' % port
flag=True
break
except:
print '%d is free' % port
flag=False
return flag
def getLocalIp():
localIP = socket.gethostbyname(socket.gethostname())
return localIP
def checkNinePort(startPort):
flag = True
ipList = ("127.0.0.1","0.0.0.0",getLocalIp())
for i in range(1, 10):
if (isInuse(ipList, startPort)):
flag = False
break
else:
startPort = startPort + 1
return flag, startPort
def findPort(startPort):
while True:
flag, endPort = checkNinePort(startPort)
if (flag == True): #ninePort is ok
break
else:
startPort = endPort + 1
return startPort
def main():
startPort=51988
# startPort = int(sys.argv[1])
print findPort(startPort)
main()

字符串匹配

第一种方法的准确性依赖于 connect((ip, int(port))) 中的 ip,

需要找到完备的 ip 集合的, 才可以确定这个端口不被占用,难度较大

思路

**在 linux 用 netstat -tnpl 可以得到端口监听信息,

观察 tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 11563/java

出现了 0 0.0.0.0:8080 所以 8080端口是被占用的


对这些输出的字符信息进行搜索 :8080, 如果存在, 就表示 8080 端口是LISTEN.

如果输出结果中不存在 :8080 的相关字符,表示这个端口不被占用.

代码

WINDOWS、 LINUX 这几个平台查看端口信息的方式不同,

先进行机器平台的判断,然后调用各个平台的端口占用判断函数

因为需求是找出连续的空闲端口,当中途只要有一个端口占用, 就跳出循环

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import os
import platform
import sys
def isInuseWindow(port):
if os.popen('netstat -an | findstr :' + str(port)).readlines():
portIsUse = True
print '%d is inuse' % port
else:
portIsUse = False
print '%d is free' % port
return portIsUse
def isInuseLinux(port):
#lsof -i:8080
#not show pid to avoid complex
if os.popen('netstat -na | grep :' + str(port)).readlines():
portIsUse = True
print '%d is inuse' % port
else:
portIsUse = False
print '%d is free' % port
return portIsUse
def choosePlatform():
#'Windows-7-6.1.7601-SP1'
#'linux-2.6.32-431.23.3.el6.x86_64-x86_64-with-centos-6.5-final'
machine = platform.platform().lower()
if 'windows-' in machine:
return isInuseWindow
elif 'linux-' in machine:
return isInuseLinux
else:
print 'Error, sorry, platform is unknown'
exit(-1)
def checkNinePort(startPort):
isInuseFun = choosePlatform()
nineIsFree = True
for i in range(1, 10):
if (isInuseFun(startPort)):
nineIsFree = False
break
else:
startPort = startPort + 1
return nineIsFree, startPort
def findPort(startPort):
while True:
flag, endPort = checkNinePort(startPort)
if (flag == True): # ninePort is ok
break
else:
startPort = endPort + 1
return startPort
def main(startPort):
firstPort=findPort(startPort)
print 'First port of nine free ports is ', firstPort
if __name__ == '__main__' :
if len(sys.argv) > 1:
print len(sys.argv)
startPort = int(sys.argv[1])
else:
startPort = 500
main(startPort)

相关知识点总结

1
os.popen()

可以调用系统的一些shell命令

1
os.popen().readlines()

读取调用shell命令后的回显信息

1
2
3
4
5
6
7
8
9
10
11
netstat -tnpl
-tnpl 各个参数的含义
-l或--listening 显示监控中的服务器的Socket。
-n或--numeric 直接使用IP地址,而不通过域名服务器。
-p或--programs 显示正在使用Socket的程序识别码和程序名称。
-t或--tcp 显示TCP传输协议的连线状况
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 9890/nginx
最后的9890/nginx 表示 进程号 9890 进程名 nginx

程序员开发过程中值得推荐的常用工具

前言

工欲善其事,必先利其器。下面总结了这些年开发中使用的一些还不错的小工具,希望能够提高编码效率

本文链接 http://www.alijava.com/common-tools/ 转载请注明出处

办公常用

7 zip

优点在于开源、无广告、免费,缺点界面有点丑

在线流程图

可以通过这个网站 https://www.processon.com/ 在线制作流程图

当然 visio 更加专业了,可惜收费

编辑器

在线 Markdown

https://stackedit.io/editor

支持实时预览,自带实时保存效果,不小心关了这个网页,重新打开后会恢复上次内容

Visual Studio Code

微软出的一款编辑器,很轻量,没有繁琐的功能,界面酷炫,黑色背景,支持 markdown实时预览

下载地址 https://code.visualstudio.com/download

Gitbook Editor

主要是为 Gitbook 服务,可以生成章节、目录,还可以利用 git 进行版本控制,对于团队发布整理 wiki 很有帮助,也可以用它基于 markdown 编写文档

网络

Postman

chrome 扩展,可以很方便输入数据模拟 get post 请求

Fiddler

windows 下的抓包工具,它是在 web server 和 web 客户端 之间搭了一层 代理,所有的请求都会经过它

用过的很惊艳的功能如下:

  • 重定向 远程资源文件 到 本地文件,比如一个 js 文件址url www.alijava.com/web/test.js,当无法修改服务端 js 文件或者部署到服务端 js 文件麻烦时,可以在本地修改该文件,然后将 www.alijava.com/web/test.js 重定向到本地 /usr/local/web/test_01.js
  • 设置断点修改Request Response
  • 过滤某些 url 请求、资源文件等

Charles

类似Fiddler,mac 下的抓包工具

Linux

PuTTY

终端远程连接 linux ,支持鼠标选中复制,右键粘贴

MTPuTTY

有时候需要连接多个linux机器,使用putty接环不方便。mtputty把putty当作标签显示,切换方便很多,提高效率

FileZilla

FTP客户端,图形用户界面(GUI),方便 windows 连接 linux 机器传输文件,类似的软件还有很多

WinSCP

同上,FTP客户端

VPSMate

安装结束使用 http://ip:8888 来打开 vpsmate 的后台

可以通过浏览器网页端编辑文件、修改文件等,可视化操作

AppNode

上面的 vpsmate 停止更新了,现在有个升级版 AppNode

协作

SourceTree

命令行使用 git 虽然很棒,而 SourceTree 提供了图形化,可以更方便查看提交记录log、对比文件的修改

当 git 仓库 有主模块、子模块,注意 SourceTree 可能会分支漂移,当主模块切到某个分子,子模块可能并没有切到某个分支,这时候需要手动切下子模块。

深入解析 Java 使用==或equals()比较字符串

Java 字符串常量

Java 字符串在面试、笔试中都是常考知识点,下面进行相关知识点讲解。

例子

1
2
3
4
5
6
7
8
9
String strOne = "testString";
String strTwo = "testString";
System.out.println(strOne.equals(strTwo));
System.out.println(strOne == strTwo);
/** output 输出
true
true
*/

结果分析

String的equals()是比较字符串的内容。故equals得到true很容易理解。

==是比较内存地址的,第一个true说明strOne 和strTwo的地址相同。

为什么呢?

java有常量池,存储所有的字符串常量。

String strOne = “testString”
java首先会在常量池查找是否有 “testString”这个常量,发现没有,于是创建一个 “testString”,然后将其赋给strOne。

String strTwo= “String”;
java同样会在常量池中查找 “testString”,这次常量池中已经有strOne 创建的 “testString”,故不创建新的常量,将其地址赋给strTwo。

如此,strOne和strTwo便有了相同的地址。

new建立String对象

Java 字符串对象创建有两种形式:

  • 上文的字符串常量,如String str = "testString"
  • 使用用new这种标准的构造对象的方法,如String str = new String("testString")

举例说明 1

1
2
3
4
5
6
7
8
9
String strOne = "testString";
String strThree = new String("testString");
System.out.println(strOne.equals(strThree));
System.out.println(strOne==strThree);
/** output 输出
true
false
*/

举例说明 2

1
2
3
4
5
6
7
8
9
String strOne = new String("testString");
String strThree = new String("testString");
System.out.println(strOne.equals(strThree ));
System.out.println(strOne==strThree);
/** output 输出
true
false
*/

分析原因

而用new String(“testString”)创建的两个字符串,用equals()比较相等,用==比较则不相等。

为什么呢?

new String()每次会在堆中创建一个对象,每次创建的对象内存地址都不同,故==不相等。但字符串内容是相同的,故equals()相等。

String intern() 方法

看下官方文档的说明,大致意思是:

如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。

1
2
3
4
5
6
7
8
9
10
11
12
public String intern()
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.
Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

举例说明 1

1
2
3
4
5
6
7
String strOne = "testString";
String strThree = new String("testString");
System.out.println(strOne==strThree.intern());
/** output 输出
true
*/

String strOne = "testString" 这行代码使得常量池中已经存在了"testString",intern() 从字符串常量池中查询到当前字符串已经存在,返回常量池中的字符串

举例说明 2

1
2
3
4
5
6
7
String strOne = new String("testString");
String strThree = new String("testString");
System.out.println(strOne.intern()==strThree.intern());
/** output 输出
true
*/

如文档里所说 s.intern() == t.intern() is true if and only if s.equals(t) is true

strOne.intern() 先查找常量池中是否存在当前字符串,发现不存在,于是会把字符串放入常量池中。strThree.intern()也先从常量池中查找,找到了刚才放入的字符串,所以两者相等。

底层实现

new 创建的String对象使用intern方法,intern方法,若不存在就会将当前字符串放入常量池中。

intern() 底层实现也是维护了一个hash表,保持字符串。

1
2
3
4
5
6
7
8
9
10
11
oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
unsigned int hashValue = java_lang_String::hash_string(name, len);
int index = the_table()->hash_to_index(hashValue);
oop string = the_table()->lookup(index, name, len, hashValue);
// Found
if (string != NULL) return string;
// Otherwise, add to symbol to table
return the_table()->basic_add(index, string_or_null, name, len,
hashValue, CHECK_NULL);
}

查看源码可知,intern() 是使用 jni 调用c++实现的StringTable的intern方法, StringTable的intern方法跟Java中的HashMap的实现是差不多的, 只是不能自动扩容。默认大小是1009。

注意,数据量规模很大时,如果程序将很多字符串常量(类名、方法名、key值等)存入intern()的常量池,当池大小超过了默认值后,性能可能会急剧下降。

String 真的不可变么

不可继承

源码可以看到 public final class String,所以 String 也不允许继承。

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
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/**
* Initializes a newly created {@code String} object so that it represents
* an empty character sequence. Note that use of this constructor is
* unnecessary since Strings are immutable.
*/
public String() {
this.value = "".value;
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}

不可改变

如果对一个已有字符串进行修改,不会在原来内存地址上修改数据,而是重新指向一个新的对象。

1
2
3
4
5
6
7
String x = "123456";
x.substring(0, 3);
System.out.println(x);
/** output 输出
123456
*/

使用 substring 改变字符串,也是返回一个新的字符串,而不是原地修改。

利用反射改变字符串

String 内部使用 private final char value[]; 来保存值,而且也没有暴露关于value的引用。虽然数组value一旦赋值后,不允许修改,但是这仅仅是不能指向新的数组,数组的内容其实可以修改。

我们可以利用反射得到 value ,从而改变 value 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String x = "123456";
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set(x, "helloworld".toCharArray());
// 可以利用set改变value的值,final char value[] 不起作用?
System.out.println(x);
char[] value = (char[]) f.get(x);
// 得到value的引用,修改数据中的某个元素
value[0] = 'H';
value[1] = 'E';
System.out.println(x);
/** output 输出
helloworld
HElloworld
*/

看下面这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String x = "123456";
String y = "123456";
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set(x, "helloworld".toCharArray());
System.out.println(x);
System.out.println(y);
char[] value = (char[]) f.get(x);
value[0] = 'H';
value[1] = 'E';
System.out.println(x);
System.out.println(y);
/** output 输出
helloworld
helloworld
HElloworld
HElloworld
*/

xy 本来都是"123456",我们只利用反射改变了x的值,却发现y的值也跟着变了,因为这两者都是指向常量值中的字符串,所以修改一个,另外一个也变了。

不推荐实际生产环境中使用反射改变字符串,违背String不可变行

参考文章

Java程序员的基本语法知识面试题

软引用 VS 弱引用

  • 软引用 在内存不足时回收

  • 弱引用 在垃圾回收时不管内存是否充足都回收

进程和线程的区别

  1. 一个程序至少一个进程,一个进程最少一个线程
  2. 进程间相互独立,同一个进程的各线程共享内存,某个进程内的线程在其他进程中不可见
  3. 操作系统中,一般把进程作为分配资源的基本单位,把线程作为独立运行和独立调度的基本单位

java 中 关键字 final 的理解

  1. 变量: 一旦初始化,变量值就不能修改
  2. 方法: 该方法不能被子类重写 ( override )
  3. : 该类不能派生子类
    比如 public final class String extends Object

java 中 finally 关键字

finally 常于 trt/catch 语句块配对使用,即使有异常抛出,也能确保某段代码一定能执行。

finally 常用于关闭流资源或者数据库连接等。

overload 和 override 的区别

  1. overload 重载,是指两个方法的名称相同,但参数类型或者个数不同,即方法的签名不同。注意:方法的返回值不能作为其签名

  2. override 重写,是指某个方法与父类的方法拥有相同的名称和函数签名。比如实现某个接口,需要进行 override 其中的抽象方法

equal() 和 hashCode() 理解

  • equal() 相等,hashCode() 必定相同

原因:

java按照hashCode来访问小内存块,所以hashCode必须相等。HashMap获取一个对象是比较key的hashCode相等和equal为true。HashMap比较对象时先通过hashcode缩小范围,然后再使用equal方法比较是否相等

  • 但是 hashCode() 相同,未必 equal() 相等

原因:

比如对象 A 和 对象 B 有 id 和 name,hashCode 按照name 来计算,但是 equal 按照 id来进行比较. 可能出现 name 相同,id 不同的情况. (公司员工同名同姓的情况). 这时候 hashCode 相同,equal 不同

java interface 接口 成员函数 可以是 private 吗

成员函数只有声明,没有方法体,自动都是 public 修饰的 (我们 code 时候 可以省去),成员变量默认都是静态变量 static final。

interface 和 抽象类

抽象类更相当于 is-a (是不是) 的关系 。接口是 has-a (有没有)的关系

比如抽象类 Bird 和 Plane 表示鸟、飞机,可以继承它们实现更具体的鸟(老鹰、麻雀等),更具体的飞机(直升机、战斗机)。

用 Fly 表示接口,一组抽象的飞行行为,不能设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。

Collection和Collections的区别

java.util.Collection 和 java.util.Collections

Collection 是接口
The root interface in the collection hierarchy. A collection represents a group of objects,


Collections 里面包含了一些 处理 集合的静态方法
This class consists exclusively of static methods that operate on or return collections.

HashSet,TreeSet 和 LinkedHashSet 的区别

  • HashSet无序

  • LinkedHashSet 按照 插入顺序 排序,使用链表维护元素的次序。

  • TreeSet是SortedSet接口的唯一实现类,有序,TreeSet支持两种排序方式,自然排序 或者 定制排序

Comparable Comparator 区别

java.lang.Comparable 和 java.util.Comparator

从英文名字也可以看出,Comparable是一种 able,表示该对象可以比较,是 对象自身的属性,其中要Override的方法 compareTo,compareTo 是 同种对象 之间 比较。

Comparator是一个名词,如果一个对象没有实现 Comparable 时,就要外部实现 一个 单独的 Comparator 接口,传递给 sort 方法,其中要Override的方法 是 compare,compare 像是 外部 对两个对象比较。

sort(Object[]),所有对象都必须实现Comparable接口,它用来确定对象之间的大小关系 。

sort(Object[],Comparator) 对象不必实现Comparable接口,各个对象根据 Comparator 确定大小关系。