熊二的三分田

种瓜得瓜 种豆得豆


  • 首页

  • 归档

  • 标签

  • 分类

  • 公益 404

通过Privoxy把socks5代理转换成http代理

发表于 2020-02-19 18:33:20 | 更新于 2020-02-19 21:45:19 | 分类于 Mac

通过Privoxy把socks5代理转换成http代理

最近玩上了trojan,据说安全性超过了SS和SSR,至少目前电信没给我打电话通知我家里网络状况异常了。trojan比较新,客户端比较少,在Mac上能用,但是只能提供sock5代理。在一些软件里比如iTerm、Android Studio里都有对https代理的需求。今天我学着别人通过Privoxy将socks5代理转换成http代理。

安装Privoxy

Privoxy是一个 HTTP 协议过滤代理,常结合 Tor 使用。Privoxy 是有着先进的过滤能力和保护隐私的代理工具,它可以过滤网页内容,管理cookies,控制访问,除广告、横幅、弹出窗口等等,它同时支持单系统和多用户网络。
当用户直接使用 SOCKS 代理访问网络时,浏览器会泄漏 DNS 请求,降低匿名性,这时应该使用 Privoxy。

Privoxy is a non-caching web proxy with advanced filtering capabilities for enhancing privacy, modifying web page data and HTTP headers, controlling access, and removing ads and other obnoxious Internet junk.

通过brew来安装Privoxy

1
2
3
4
5
6
7
8
9
10
11
12
-> brew install privoxy
Updating Homebrew...
==> Downloading https://mirrors.ustc.edu.cn/homebrew-bottles/bottles/privoxy-3.0
Already downloaded: /Users/ted/Library/Caches/Homebrew/downloads/6d09ad8bd36fc3b523c9067d81fffe0f767258dc0e85295b97d0e0110df9dbce--privoxy-3.0.28.catalina.bottle.tar.gz
==> Pouring privoxy-3.0.28.catalina.bottle.tar.gz
==> Caveats
To have launchd start privoxy now and restart at login:
brew services start privoxy
Or, if you don't want/need a background service you can just run:
privoxy /usr/local/etc/privoxy/config
==> Summary
🍺 /usr/local/Cellar/privoxy/3.0.28: 98 files, 2MB

配置Privoxy

按照安装时提示的配置文件路径 /usr/local/etc/privoxy/config ,在尾部增加配置如下:

1
2
3
4
forward-socks5 / localhost:1180 . #设置转发到本地的socks5代理客户端端口
listen-address 0.0.0.0:8080 #privoxy暴露的HTTP代理地址,设置 privoxy 监听任意 ip的8080端口
forward 10.*.*.*/ . #内网地址不走代理
forward .abc.com/ . #指定域名不走代理

启动Privoxy

按照安装时提示文字,可以通过 brew services start privoxy 来后台运行Privoxy并且配置开机启动,也可以通过 sudo /usr/local/sbin/privoxy /usr/local/etc/privoxy/config 来临时运行Privoxy。

检测是否运行

可以通过 ps aux | grep privoxy 和 lsof -i:8080 来检测Privoxy是否在运行中,其中8080是上面config中配置的监听端口。

查看是否监听端口

1
netstat -na | grep 8080

如下图表示端口正在监听:
202002192110-1582117845807

退出Privoxy

通过 ps aux | grep privoxy 获取到对应的进程ID,通过 kill 进程ID 的方式即可退出Privoxy。

配置Git走代理

全局配置

1
2
git config --global http.proxy http://127.0.0.1:8080
git config --global https.proxy http://127.0.0.1:8080

全局配置取消

1
2
git config --global --unset http.proxy
git config --global --unset https.proxy

针对网址配置

1
2
git config --global http.https://github.com.proxy socks5://127.0.0.1:1180
git config --global https.https://github.com.proxy socks5://127.0.0.1:1180

针对网址取消

1
2
git config --global --unset http.https://github.com.proxy 
git config --global --unset https.https://github.com.proxy

配置全局走代理

直接在.zshrc或者.bash_profile中加入以下代码,source之后,通过对应的方法即可快速开启或者关闭全局代理。

1
2
3
4
5
6
7
8
9
10
11
12
function openProxy() {
export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"
export http_proxy="http://127.0.0.1:8080"
export https_proxy=$http_proxy
echo -e "已开启代理"
}

function closeProxy() {
unset http_proxy
unset https_proxy
echo -e "已关闭代理"
}

全局代理测试

  1. 打开全局代理之后,在iTerm中输入 curl cip.cc , 如果显示的是代理服务器的相关地址信息,就说明代理成功了。
  2. 在iTerm中输入 curl -i https://google.com , 有结果输出,也说明代理成功了。
    202002192144-1582119897596

参考文章:

  1. Mac 终端通过 privoxy 科学上网
  2. 给 iTerm 设置代理

博客首页

发表于 2018-07-01 10:48:37 | 更新于 2020-02-19 19:30:14 | 分类于 blog

基础服务

服务 状态 描述
网址导航 Runing 一个静态网址导航页面
家庭云 Runing -
家庭路由器 Runing -
迅雷远程下载 Runing -
家庭流媒体中心 Runing 账号:xiong-wei@hotmail.com
Gogs服务器 Runing -
碎片记录 Runing 不成文的一些知识点记录
旧版博客 Closed 托管在GitHub上的老版博客
书签备份 Runing -

联系我

联系方式 内容 联系时间
Email xiong-wei@hotmail.com 24H
Wechat Screenshot 08:00-20:00
Tel 17621163178 08:00-20:00
我的简历 中文版 区块链版

我的读书清单

书名 状态 笔记 资源
Effective Java中文版 Pending - Book + Ebook
Kotlin for Android Developers Pending - Ebook
Algorithms Reading GitBook Book
区块链 技术驱动金融 Reading - Book
区块链革命 Reading - Book
区块链技术进阶与实践 Reading - Book

常去的博客

博客地址 侧重方向 备注
zjutkz’s blog Android -
代码家 Android\区块链 -
GitYuan Android源码分析 -
baronzhang Android组件化 -
commonsware Android相关 -
jessyan.me Android相关 -
diglp.xyz 区块链相关 -

资源类网站推荐

入口 类别 描述
Neets 美剧 美剧垂直搜索引擎
电影蜜蜂 电影资源 各种高清电影资源

好玩的地方

地址 描述 备注
Leetcode 算法学习 -
九章算法 算法学习 -

RxJava2 入门教程

发表于 2018-06-20 15:29:19 | 更新于 2018-07-16 21:20:13 | 分类于 rxjava

RxJava2 入门教程

背景

在一个舒适区呆久了,要么就废掉,要么就挣扎着跳出来。因为各种原因,过去的几个月并不是特别忙,趁机想研究点新技术,说是新技术其实也算不上,几年前的个人项目里就已经使用个RxJava、Retrofit和OKHttp来做网络请求架构,但是一直没有系统的去了解过什么是RxJava以及RxJava可以做什么事情,这次正好借机系统的学习一下RxJava,整理此文算是重新入门。本文不少地方参考了大神扔物线的给 Android 开发者的 RxJava 详解,只不过大神的文章是基于RxJava的,我则是基于RxJava2再重新梳理一遍,RxJava2相对于RxJava还是有不少变化的。

[DOC]

RxJava是什么

一千个人就有一千个哈姆雷特,关于RxJava的理解,可能每个人的理解多少有点差别,但是核心肯定是一样的。基于官方的解释,RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.,翻译过来就是“RxJava,Java虚拟机的响应式扩展,一个运行在java虚拟机上的库,通过可观察的队列来实现异步的、基于事件化的程序”。我的英文有点烂,凑合着翻译成这样了。几个核心关键词很重要,响应式、可观察的队列、异步、事件化。

响应式

什么是响应式,或者说什么是响应式编程,第一次接触到这个概念的时候很糊涂,难道还有非响应式么?程序不是都会响应么?不响应了不就是ANR了吗?要理解响应式,必须得配合另外一个关键词一起理解,数据流。最开始在做地图上标记maker的功能时,我自己想到过数据流这个概念,那时候还没接触到响应式编程,当时我把查询数据的代码封装成“事件数据源”,源源不断的释放出新的数据出来,经过各种过滤处理,比如说数据是否有效?数据量是否超载?是否有重复数据等等一系列的过滤之后,交给“事件消耗方”,即绘制在地图上。可以把数据流理解为一条河流,经过不断的过滤,操作甚至分叉之后,被消耗掉或者跟更多新的数据汇聚成新的一条河流。

而这里说到的数据其实就是一个个事件,比如点击一下屏幕、收到一个推送、我们执行一个SQL查询等,都可以抽象成一系列事件,而CPU处理器就是在不停的通过调度处理各种各样的事件运算。这些事件组成了数据流,我们所说的响应式编程,就是说对这些事件流做出反应。点击屏幕————>点亮手机,收到推送————>显示notification,SQL查询————>处理查询结果————>显示到UI上面。诸如此类,基于数据流和变化的一种编程范式就叫做响应式编程,把一切行为归纳为数据流,通过各种函数进行合并、过滤、转变等等。
流是响应式的核心,下面就是 “在按钮上点击” 事件流的简单示意图。
20180628153015267661416.png
关于更多的响应式编程可以查看:The introduction to Reactive Programming you’ve been missing · GitHub

异步

这是RxJava主要的核心功能,它能够轻松的实现异步操作,比如SQL查询,以往的经验是开一个新的线程执行查询代码,结果返回后通知主线程更新UI。例如如下的实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
new Thread() {
@Override
public void run() {
super.run();
//execute sql query
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
//update UI
}
});
}
}.start();

从代码上看,查询和更新UI两个行为通过嵌套的形式存在,倒也清晰。我们再换成RxJava实现看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) {
//execute sql query
Object dbResult = new Object();//模拟查询结果
emitter.onNext(dbResult);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object dbResult) {
//update UI
}
});

单从代码量上来看,使用RxJava实现的代码似乎是多于传统写法的,如果你习惯Lambda表达式的话,以上代码可以再精简一番,如下

1
2
3
4
5
6
7
8
9
10
Observable.create(emitter -> {
//execute sql query
Object dbResult = new Object();//模拟查询结果
emitter.onNext(dbResult);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(dbResult -> {
//update UI
});

仔细看代码的话,会发现使用RxJava实现的代码都是链式调用,从上往下依次执行。而传统写法的代码,则是通过嵌套方式实现。一两层嵌套可能没什么影响,但是如果嵌套比较多的时候,比如请求网络数据之后需要查询数据库对比数据,经过复杂运算之后,再更新到UI上面。这时候RxJava的优势就体现出来了,把整个过程组成一个链,逐次执行,业务逻辑清晰,代码可读性也比较高。最讨厌那些各种代码缩进了,嵌套一多,缩进就会多的很恶心。

回到本小节主题上,关于异步,RxJava如何实现呢?上面代码里其实已经有所体现,就是观察者模式,RxJava把观察者模式体现的淋漓尽致。提到观察者模式,很多Android工程师第一想到的就是事件监听,通过对一个View添加一个监听器,即可实现一个订阅者关系。一旦View被点击了,OnClickListener即可获得到回调。而RxJava则是进行了更深一步的抽象,把可以触发点击事件的View抽象成为Observable(可观查的,即被观察者),而OnClickListener则被抽象成为Observer(观察者)。同时还有另外一步抽象,就是把setOnClickListener这个过程抽象成subscribe(订阅)。
简单画了个对比图,如下:
20180628153015571191248.png

可观察的队列 和事件化

经过上面两个核心点的分析,可观察的队列理解起来并不难,在RxJava中每一步对事件流的加工组合成一个队列,依次执行,并且这个队列的每一步都是可以观察的,通过观察可以对执行结果进行处理。
而对于事件化,everything can be a stream,几乎一切事情都可以被事件流化,这就是响应式编程中的一句名言。

如何使用RxJava2

对于学习一门新的技术来说,我认为首先要先用最快的速度了解这门技术是什么,能干什么?比如通过维基百科或者一些使用母语写的文章熟悉基本概念,然后通过官方文档熟悉具体细节。经过上面一些篇幅介绍了什么是RxJava,接下来我将通过翻译RxJava官方的教程来梳理如何使用RxJava2。

添加依赖

只需要在你项目里添加如下Gradle编译依赖,即可使用RxJava2:

`compile "io.reactivex.rxjava2:rxjava:2.x.y"`

写此文时最新版本是2.1.16,所以需要替换上面脚本里对应的x、y。

Hello World

通过以下代码即可实现最简单的打印Hello World功能:

1
2
3
4
5
6
7
8
9
package rxjava.examples;

import io.reactivex.*;

public class HelloWorld {
public static void main(String[] args) {
Flowable.just("Hello world").subscribe(System.out::println);
}
}

如果你的平台不支持Java 8 lambda表达式,你需要手动实例化一个内部类Consumer作为消费者。

1
2
3
4
5
6
7
8
import io.reactivex.functions.Consumer;

Flowable.just("Hello world")
.subscribe(new Consumer<String>() {
@Override public void accept(String s) {
System.out.println(s);
}
});

基本类型

RxJava2提供了一些基本的操作符类型:

  • io.reactivex.Flowable: 事件流,支持响应式事件流和背压功能,比较复杂的一个操作符。
  • io.reactivex.Observable: 事件流,不支持背压。基本上可以满足大部分需求。
  • io.reactivex.Single: 仅支持一个正常事件或者异常事件。成功or失败,二选一。
  • io.reactivex.Completable: 仅仅关心任务完成或者异常,不关心数据。完成or失败,二选一。
  • io.reactivex.Maybe: 支持0或者1个数据,要么成功,要么失败。成功or完成or失败,三选一。

一些术语

Upstream, downstream 上游流,下游流

在RxJava中,数据流由三部分组成,数据源 + 一个或多个中间处理步骤 + 数据消费者或者再加工者。

1
2
3
source.operator1().operator2().operator3().subscribe(consumer);

source.flatMap(value -> source.operator1().operator2().operator3());

那么,如果我们处在operator2这里,向左看的话,左边的被称作上游流,右边的订阅者/消费者就被称作为下游流。当每一步骤的代码单独写到一行时,看起来更加直观。

1
2
3
4
5
source
.operator1()
.operator2()
.operator3()
.subscribe(consumer)

Objects in motion 运动中的对象

在RxJava开发文档中, emission, emits, item, event, signal, data and message 被认为是同义词,他们用来表示沿着数据流传播的对象。

Backpressure 背压

当数据流通过异步方式运行,每一步可能以不同的速度执行不同的任务。为了防止某一步骤超过负载而导致出现各种问题,背压功能出现了。通过背压功能可以做好流程控制,确定当前步骤可以处理多少任务。这样就可以在无法得知上游发送多少数据的情况下限制内存使用。
在RxJava中只有Flowable类支持使用背压,而Observable被设计为无背压操作符,比如小的任务队列、GUI交互等等。至于其他类型,Single, Maybe 和 Completable不支持背压,当然也不应该支持。他们都仅仅操作一个事件而已,有足够的空间。

Assembly time 组装时间

事件流在准备期间,通过一系列的中间操作对数据加工,被称为组装时间:

1
2
3
4
Flowable<Integer> flow = Flowable.range(1, 5)
.map(v -> v* v)
.filter(v -> v % 3 == 0)
;

这个时候事件并没有流动起来,也没有其他事情发生。

Subscription time 订阅时间

这是一个临时状态,发生在 subscribe()被调用的时候。

1
flow.subscribe(System.out::println)

这时候订阅的接口会被触发(参考doOnSubscribe),同时一些代码块会被执行或者一些事件会被发出。

Runtime 运行时

当事件正在被发出,或者错误信号、完成信号被发出的时候,叫做运行时状态。

1
2
3
4
5
6
7
8
9
10
11
Observable.create(emitter -> {
while (!emitter.isDisposed()) {
long time = System.currentTimeMillis();
emitter.onNext(time);
if (time % 2 != 0) {
emitter.onError(new IllegalStateException("Odd millisecond!"));
break;
}
}
})
.subscribe(System.out::println, Throwable::printStackTrace);

在上面代码中,当while开始执行时,就是运行时状态。

后台线程

使用RxJava比较常见的一个场景是:在后台线程进行运算、执行网络请求,然后把结果数据或者错误信息展示在UI线程上。

1
2
3
4
5
6
7
8
9
10
11
import io.reactivex.schedulers.Schedulers;

Flowable.fromCallable(() -> {
Thread.sleep(1000); // 模拟复杂的运算
return "Done";
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(System.out::println, Throwable::printStackTrace);

Thread.sleep(2000); // <--- 等待事件流完成

这种类型的链接方法称为流畅的API,类似于构建器模式。但是,RxJava的响应式类型是不可变的; 每个方法调用都会返回一个具有添加行为的新Flowable。仅仅为了演示,上面代码可以写成如下这样:

1
2
3
4
5
6
7
8
9
10
11
12
Flowable<String> source = Flowable.fromCallable(() -> {
Thread.sleep(1000);
return "Done";
});

Flowable<String> runBackground = source.subscribeOn(Schedulers.io());

Flowable<String> showForeground = runBackground.observeOn(Schedulers.single());

showForeground.subscribe(System.out::println, Throwable::printStackTrace);

Thread.sleep(2000);

通常,你可以把一些运算或者IO阻塞的行为通过subscribeOn运行在其他线程上面,一旦数据准备好了,你可以再通过observeOn来确保他们运行在前台线程或者GUI线程上。

线程调度器

RxJava操作符里不能直接使用Thread或者ExecutorService,而是通过使用Scheduler来实现并发和线程切换。RxJava2具有以下几个标准的线程调度器,可以通过Schedulers工具类来使用。

  • Schedulers.computation(): 在后台对固定数量的专用线程运行计算密集型工作。通常作为异步操作符的默认调度器。
  • Schedulers.io(): IO阻塞操作,有不定数量的线程。
  • Schedulers.single(): 以顺序和FIFO方式在单线程上运行工作
  • Schedulers.trampoline(): 在一个参与线程中以顺序和FIFO方式运行工作,通常用于测试目的。

流中的并发性

并行处理

依赖子流

延续性

类型转换

操作符命名约定

进一步阅读

Golang 学习笔记-基础(二)

发表于 2018-06-14 16:30:27 | 更新于 2018-07-16 21:20:13 | 分类于 golang

Golang 学习笔记-基础(二)

0x0 for循环

  • Go 只有一种循环结构:for 循环。
    基本的 for 循环由三部分组成,它们用分号隔开:
    初始化语句:在第一次迭代前执行
    条件表达式:在每次迭代前求值
    后置语句:在每次迭代的结尾执行
    初始化语句通常为一句短变量声明,该变量声明仅在 for 语句的作用域中可见。
    一旦条件表达式的布尔值为 false,循环迭代就会终止。
    注意:和 C、Java、JavaScript 之类的语言不同,Go 的 for 语句后面没有小括号,大括号 { } 则是必须的。
1
2
3
4
5
6
7
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
  • 另外,表达式中初始化语句和后置语句是可选的,比如可以写成
1
2
3
4
5
6
7
func main() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}
  • 也可以把for当做while使用,比如
1
2
3
4
5
6
7
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
  • 如果省略判断表达式,就会进入无限死循环

0x1 if 判断

  • if表达式外无需小括号,但是大括号是必须的
  • if可以在条件表达式之前执行一个简单的短句,该短句声明的变量作用域仅在if之内
1
2
3
4
5
6
7
8
9
10
11
12
13
func testIf(value int) bool {
if temp := value % 2; temp == 0 {
return true
}
return false
}

func main() {
fmt.Println(
testIf(11),
testIf(10),
)
}
  • 在 if 的简短语句中声明的变量同样可以在任何对应的 else 块中使用。

0x2 switch

switch 是编写一连串 if - else 语句的简便方法。它运行第一个值等于条件表达式的 case 语句。
Go 只运行选定的 case,而非之后所有的 case。 Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。 除非以 fallthrough 语句结束,否则分支会自动终止。
另一点重要的不同在于 switch 的 case 无需为常量,且取值不必为整数。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}

case后面也能是执行语句,并且从上到下顺序执行,知道匹配成功为止。
没有条件的 switch 同 switch true 一样。

1
2
3
4
5
6
7
8
9
10
11
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}

这种形式能将一长串 if-then-else 写得更加清晰。

0x3 defer

defer 语句会将函数推迟到外层函数返回之后执行。
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。

1
2
3
4
5
6
7
8
9
func main() {
arg := 10
defer fmt.Println("defer method ", arg)
arg++
fmt.Println("main method", arg)
}
//output
//main method 11
//defer method 10

推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
fmt.Println("counting")

for i := 0; i < 10; i++ {
defer fmt.Println(i)
}

fmt.Println("done")
}
//output
//counting
//done
//9
//8
//7
//6
//5
//4
//3
//2
//1
//0

0x4 指针

Go 拥有指针。指针保存了值的内存地址。
类型 *T 是指向 T 类型值的指针。其零值为 nil。
var p *int
& 操作符会生成一个指向其操作数的指针。
i := 42
p = &i
& 操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
这也就是通常所说的“间接引用”或“重定向”。
与 C 不同,Go 没有指针运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
i, j := 7, 8

p := &i // point to i
fmt.Println(*p) // read i through the pointer
*p = 11 // set i through the pointer
fmt.Println(i) // see the new value of i

p = &j // point to j
*p = *p / 2 // divide j through the pointer
fmt.Println(j) // see the new value of j
}
//output
//7
//11
//4

0x5 结构体

一个结构体(struct)就是一个字段的集合。
结构体字段使用点号来访问。
结构体字段可以通过结构体指针来访问。如果有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。也允许我们使用隐式间接引用,直接写 p.X 就可以。

0x6 数组

类型 [n]T 表示拥有 n 个 T 类型的值的数组。
表达式
var a [10]int
会将变量 a 声明为拥有有 10 个整数的数组。
数组的长度是其类型的一部分,因此数组不能改变大小。Go 提供了更加便利的方式来使用数组。

0x7 切片

  • 每个数组的大小都是固定的。而切片则为数组元素提供动态大小的、灵活的视角。在实践中,切片比数组更常用。
    类型 []T 表示一个元素类型为 T 的切片。
    切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:
    a[low : high]
    它会选择一个半开区间,包括第一个元素,但排除最后一个元素。
    以下表达式创建了一个切片,它包含 a 中下标从 1 到 3 的元素:
    a[1:4]
1
2
3
4
5
6
7
8
func main() {
primes := [6]int{2, 3, 5, 7, 11, 13}

var s []int = primes[1:4]
fmt.Println(s)
}

//output [3 5 7]
  • 切片就像数组的引用
    切片并不存储任何数据,它只是描述了底层数组中的一段。
    更改切片的元素会修改其底层数组中对应的元素。
    与它共享底层数组的切片都会观测到这些修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)

a := names[0:2]
b := names[1:3]
fmt.Println(a, b)

b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}

//output
//[John Paul George Ringo]
//[John Paul] [Paul George]
//[John XXX] [XXX George]
//[John XXX George Ringo]
  • 切片的默认上下标分别是0和切片长度,对于a [10]int数组来说,以下切片等价
    a[0:10]
    a[:10]
    a[0:]
    a[:]

  • 切片的长度和容量
    一个切片底层对应的是一个数组,无论切片如何变化,对应的数组是一个。那么切片的长度就是其包含的元素个数,切片的容量则是切片的第一个元素开始到数组的最后一个元素为止的元素个数。

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
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)

// Slice the slice to give it zero length.
a := s[:0]
printSlice(a)

// Extend its length.
b := s[:4]
printSlice(b)

// Drop its first two values.
c := s[2:]
printSlice(c)
}

func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

//output
//len=6 cap=6 [2 3 5 7 11 13]
//len=0 cap=6 []
//len=4 cap=6 [2 3 5 7]
//len=4 cap=4 [5 7 11 13]
  • nil 切片
    切片的零值是nil,长度和容量都是0,没有底层数组。

  • make创建切片
    切片可以用内建函数 make 来创建,这也是你创建动态数组的方式。
    make 函数会分配一个元素为零值的数组并返回一个引用了它的切片:
    a := make([]int, 5) // len(a)=5
    要指定它的容量,需向 make 传入第三个参数:
    b := make([]int, 0, 5) // len(b)=0, cap(b)=5

  • 切片的切片
    切片可包含任何类型,甚至包括其它的切片。

  • 切片的append真是一个坑啊

Golang 学习笔记-基础(一)

发表于 2018-06-13 09:28:10 | 更新于 2018-07-16 21:20:13 | 分类于 golang

Golang 学习笔记-基础(一)

0x0 包

每个 Go 程序都是由包构成的。
程序从 main 包开始运行。

0x1 导入

使用圆括号,分组导入依赖包。

1
2
3
4
import (
"fmt"
"math"
)

0x2 导出名称

如果一个名字以大写字母开头,那么它就是已导出的。例如,Pizza 就是个已导出名,Pi 也同样,它导出自 math 包。
pizza 和 pi 并未以大写字母开头,所以它们是未导出的。
在导入一个包时,你只能引用其中已导出的名字。任何“未导出”的名字在该包外均无法访问。

1
2
3
func main() {
fmt.Println(math.Pi)
}

0x3 函数

函数可以没有参数或接受多个参数。
在本例中,add 接受两个 int 类型的参数。
当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略

1
2
3
4
5
6
7
func add(x int, y int) int {
return x + y
}

func sub(x, y int) int {
return x - y
}

0x4 多值返回

函数可以返回任意数量的返回值。
swap 函数返回了两个字符串。

1
2
3
func swap(x, y string) (string, string) {
return y, x
}

0x5 命名返回值

Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
返回值的名称应当具有一定的意义,它可以作为文档使用。
没有参数的 return 语句返回已命名的返回值。也就是 直接 返回。

1
2
3
4
5
6
7
8
9
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
fmt.Println(split(17))
}
//output 7 10

0x6 变量

var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。
就像在这个例子中看到的一样,var 语句可以出现在包或函数级别。

1
2
3
4
5
6
7
var c, python, java bool

func main() {
var i int
fmt.Println(i, c, python, java)
}
//output 0 false false false

0x7 变量的初始化

变量声明可以包含初始值,每个变量对应一个。
如果初始化值已存在,则可以省略类型;变量会从初始值中获得类型。

1
2
3
4
5
6
7
var i, j int = 1, 2

func main() {
var c, python, java = true, false, "no!"
fmt.Println(i, j, c, python, java)
}
//output 1 2 true false no!

0x8 短变量声明

在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。

1
2
3
4
5
6
7
8
func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, "no!"

fmt.Println(i, j, k, c, python, java)
}
//output 1 2 3 true false no!

0x9 基本类型

Go 的基本类型有

1
2
3
4
5
6
7
8
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名 // 表示一个 Unicode 码点
float32 float64
complex64 complex128

同导入语句一样,变量声明也可以“分组”成一个语法块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
func main() {
fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
fmt.Printf("Type: %T Value: %v\n", z, z)
}
//output
//Type: bool Value: false
//Type: uint64 Value: 18446744073709551615
//Type: complex128 Value: (2+3i)

int, uint 和 uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽。 当你需要一个整数值时应使用 int 类型,除非你有特殊的理由使用固定大小或无符号的整数类型。

0xA 零值

没有明确初始值的变量声明会被赋予它们的 零值。
零值是:
数值类型为 0,
布尔类型为 false,
字符串为 “”(空字符串)

0xB 类型转换

表达式 T(v) 将值 v 转换为类型 T。
一些关于数值的转换:
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
或者,更加简单的形式:
i := 42
f := float64(i)
u := uint(f)
与 C 不同的是,Go 在不同类型的项之间赋值时需要显式转换。

0xC 类型推导

在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出。
当右值声明了类型时,新变量的类型与其相同:
var i int
j := i // j 也是一个 int
不过当右边包含未指明类型的数值常量时,新变量的类型就可能是 int, float64 或 complex128 了,这取决于常量的精度:
i := 42 // int
f := 3.142 // float64
g := 0.867 + 0.5i // complex128

0xD 常量

常量的声明与变量类似,只不过是使用 const 关键字。
常量可以是字符、字符串、布尔值或数值。
常量不能用 := 语法声明。

0xE 数值常量

数值常量是高精度的 值。
一个未指定类型的常量由上下文来决定其类型。
再尝试一下输出 needInt(Big) 吧。
(int 类型最大可以存储一个 64 位的整数,有时会更小。)
(int 可以存放最大64位的整数,根据平台不同有时会更少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
// Shift it right again 99 places, so we end up with 1<<1, or 2.
Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}

func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
//output
//21
//0.2
//1.2676506002282295e+29

Mac上Golang开发环境配置

发表于 2018-05-12 14:28:30 | 更新于 2018-07-16 21:20:13 | 分类于 golang

Mac上Golang开发环境配置

Golang,Google家的开发语言,碎碎念了好久,今天起开始学习一番。

安装Golang

  • 去官方地址下载安装包,安装完成后,检查版本信息。
    20180612152879252327999.png

  • 配置环境变量,我因为使用了zsh作为shell终端,所以配置在“~_.zshrc”中,在文件最后加入如下内容,”/Users/ted/Go”是我自己创建的目录,作为编译后二进制存放目录以及import包的搜索路径,也就是工作目录:

1
2
3
4
# Golang Path config
export GOPATH=/Users/ted/Go
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN

最后执行source .zshrc即可。

  • 安装gocode
    gocode是golang的自动完成提示工具,可以集成到很多IDE中,比如subl3,emacs,vim等等,

    go get -u github.com/nsf/gocode

安装Sublime Text 3

  • 下载Sublime Text 3并且安装。
  • 在Sublime Text 3上安装package control
  • 安装GoSublime插件,Preferences -> Package Control,输入GoSublime,安装即可。
  • 配置完成之后,如下:
    ok

创建项目

  • 在刚才配置的GOPATH的src目录下创建一个项目文件夹,比如HelloGo。
  • 使用Sublime Text打开文件夹,添加一个Hello.go文件,加入如下内容:
1
2
3
4
5
6
7
8
9
package main

import (
"fmt"
)

func main() {
fmt.Println("Hello, World !!!")
}
  • 在通过快捷键Command+B,进入终端,执行go build,会编译生成一个以项目名称命名的文件HelloGo,执行./HelloGo,正常输出内容即可。

其他

  • 安装格式化插件gofmt后,保存内容时,会报错提示”golang的config中gopath没有设置”,这时候在golang config文件中加入以下内容保存即可:
1
2
3
{
"GOPATH": "/Users/ted/Go"
}

Java核心技术3-强引用、软引用、弱引用、幻象引用有什么区别?

发表于 2018-05-06 10:26:39 | 更新于 2018-07-16 21:20:13 | 分类于 Java

Java核心技术3-强引用、软引用、弱引用、幻象引用有什么区别?

不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。

基本概念

强引用

Strong Reference,普遍对象的引用类型,实例化一个对象,引用跟对象所占内存之间是强关系,除非该对象没有被其他继续引用或者显式的指定该强引用为null,否则内存回收时是不会回收该对象,也就无法释放其所占有的内存。如果将一个强引用指定为null,并不一定立即被回收,还要看垃圾回收策略。

软应用

Soft Reference,比强引用稍微弱化一点的引用类型,被弱引用指向的对象在JVM内存不足之前不会被系统回收,一旦系统将要抛出oom的错误之前,会优先回收软应用所指向的对象。软应用通常用来实现内存敏感的缓存,如果还有多余内存,就不会被回收,当内存不足时候,清理掉。(城市列表信息)

弱引用

Weak Reference,并不能使对象豁免垃圾收集,仅仅是提供一种访问在弱引用状态下对象的途径。这就可以用来构建一种没有特定约束的关系,比如,维护一种非强制性的映射关系,如果试图获取时对象还在,就使用它,否则重现实例化。它同样是很多缓存实现的选择。

幻象引用

Phantom Reference也翻译成虚引用,你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制,比如,通常用来做所谓的 Post-Mortem 清理机制,比如Java 平台自身 Cleaner 机制等,也有人利用幻象引用监控对象的创建和销毁。

具体分析

强引用

特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。 当JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。

软引用

特点:软引用通过SoftReference类实现。 软引用的生命周期比强引用短一些。只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象:即JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。

应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

弱引用

弱引用通过WeakReference类实现。 弱引用的生命周期比软引用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

应用场景:弱应用同样可用于内存敏感的缓存。

幻象引用

特点:虚引用也叫幻象引用,通过PhantomReference类来实现。无法通过虚引用访问对象的任何属性或函数。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动。

应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知。

Java核心技术2-final、finally、 finalize有什么不同?

发表于 2018-05-05 19:20:38 | 更新于 2018-07-16 21:20:13 | 分类于 Java

Java核心技术2-final、finally、 finalize有什么不同?

final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以继承扩展,final 的变量是不可以修改的,而 final 的方法也是不可以重写的(override)。

finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。

finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为 deprecated。

Java核心技术1-Exception和Error

发表于 2018-05-05 16:30:20 | 更新于 2018-07-16 21:20:13 | 分类于 Java

Java核心技术1-Exception和Error

Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。 Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。

Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。

20180620152947613119068.png

Java核心技术0-Java平台

发表于 2018-05-04 13:06:28 | 更新于 2018-07-16 21:20:13 | 分类于 Java

Java核心技术0-Java平台

关键词

Compile once,Run anywhere
解释执行
垃圾回收 GC,Garbage Collection

JRE Java Runtime Environment,包含JVM和Java类库和一部分模块
JDK Java Development Kit,包含JRE以及更多工具,编译器、诊断工具等。

Java源代码->字节码->JVM解释成为机器码执行
常见的JVM,都提供了JIT,Just-In-Time编译器,也就是通常所说的动态编译器,能够在运行时将热点代码编译成机器码,这种情况下热点代码就属于编译执行,而不是解释执行了。

除了我们日常最常见的 Java 使用模式,其实还有一种新的编译方式,即所谓的 AOT(Ahead-of-Time Compilation),直接将字节码编译成机器代码。

Java特性:
面向对象(封装,继承,多态)
平台无关性(JVM运行.class文件)
语言(泛型,Lambda)
类库(集合,并发,网络,IO/NIO)
JRE(Java运行环境,JVM,类库)
JDK(Java开发工具,包括JRE,javac,诊断工具)

Java是解析运行吗?
不正确!
1,Java源代码经过Javac编译成.class文件
2,.class文件经JVM解析或编译运行。
(1)解析:.class文件经过JVM内嵌的解析器解析执行。
(2)编译:存在JIT编译器(Just In Time Compile 即时编译器)把经常运行的代码作为”热点代码”编译与本地平台相关的机器码,并进行各种层次的优化。
(3)AOT编译器: Java 9提供的直接将所有代码编译成机器码执行。

12…4
熊伟

熊伟

用技术改变生活

39 日志
16 分类
GitHub E-Mail Google Instagram 家庭云 家庭影院 路由器 代码库
Creative Commons
© 2016 — 2020 熊伟 版权所有 沪ICP备 - 17019408号
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Pisces v6.3.0
0%