Java

  • Java 编程思想(第 4 版)「阅读中」

    这算是一本 Java 必读书籍了,有一定基础后再看会好一些,可以放到桌边当工具书。

  • Effective java 中文版(第 2 版)「未阅读」

    这本介绍了 Java 中经常使用但容易被忽略的一些规则,适合对 Java 已经有一定基础的同学。

  • Java 并发编程实战「未阅读」

    多线程并发方面不可多得的好书。

  • 深入理解 Java 虚拟机(第 2 版)「阅读中」

    了解 Java 虚拟机的必读书籍。

  • Java 程序员修炼之道「未阅读」

Android

  • Android 开发艺术探索「阅读中」

  • Android 群英传「阅读完」

  • Android 群英传:神兵利器「阅读中」

  • App 研发录:架构设计、Crash 分析和竞品技术分析「阅读完」

  • Android 源码设计模式解析与实战「阅读中」

  • Android 开发精要 「未阅读」

  • kotlin-for-android-developers-zh「阅读完」

Web 前端

  • JavaScript 权威指南 第 6 版(上、下册) 「未阅读」

Python

  • Python 学习手册(第 4 版)「阅读中」

算法

  • 算法(第 4 版) 「未阅读」

    Sedgewick 之巨著,与高德纳 TAOCP 一脉相承几十年多次修订,经久不衰的畅销书,涵盖所有程序员必须掌握的 50 种算法。

设计模式

  • 设计模式之禅(第 2 版)「阅读中」

其它技术书籍

  • 代码大全(第 2 版)「阅读中」

  • 图解 HTTP「未阅读」

  • 剑指 offer「阅读完」

  • 重构 : 改善既有代码的设计「未阅读」

  • Maven 实战「未阅读」

非技术书籍

  • 程序员的思维修炼「阅读中」

  • 学习之道(第 2 版)「阅读中」

  • 如何把事情做到最好「阅读中」

  • 清醒思考的艺术「阅读中」

  • 番茄工作法图解「未阅读」

  • 习惯的力量「未阅读」

  • 尽管去做「未阅读」

  • 拖延心理学「阅读中」

Chrome 开发者工具面板简介

Mou icon

  • 箭头图标

    用于在页面选择一个元素来审查和查看它的相关信息,当我们在 Elements 标签页页面下点击某个 DOM 元素时,箭头图标会变成选择状态。

  • 设备图标

    可以选择不同的终端设备、不同的尺寸比例进行模拟开发。

  • Elements

    查找网页源代码 HTML 中的任一元素,手动修改任一元素的属性样式且能实时在浏览器里面得到反馈。

  • Console

    记录开发者开发过程中的日志信息,且可以作为与 JS 进行交互的命令行 Shell .

  • Sources

    用于查看调试当前页面所加载的脚本的源文件。

  • Network

    用于查看 HTTP 请求的详细信息,如请求头、响应头及返回内容等。

  • Performance

  • Memory

  • Application

    记录网站加载的所有资源信息,包括存储数据(Local Storage、Session Storage、IndexedDB、Web SQL、Cookies)、缓存数据、字体、图片、脚本、样式表等。

  • Security

    判断当前网站的安全性,查看有效证书等。

  • Audits

    对当前网页进行网络利用情况、网页性能方面的诊断,并给出一些优化建议。比如列出所有没有用到的 CSS 文件等。

Elements

可以查看、修改页面上的元素,包括 DOM 标签、CSS 样式,还有相关盒模型的图形信息。

  • 双击 DOM 树视图里面的节点,可以实时编辑标签属性,修改的效果会立刻反应在浏览器里面。

  • 点击右侧的 Styles 标签页,可以实时修改 CSS 的属性值,所有的 Name 和 Value 值都是可以编辑的。在每个属性后面单击可以添加新的样式。

Mou icon

  • 点击 Computed 标签页,可以编辑左侧选中的盒子模型参数,所有值都是可以修改的。点击不同的位置(top、bottom、left、right)就可以修改元素的 padding、border、margin 属性值。

注意:以上对页面上的修改并不会作用到源码上,它仅用于调试,一刷新这些修改就会复原。我们可以一次性在浏览器中做了修改后,再把改动复制到源码中。

Console

在控制台输出日志

在 JS 代码或者命令行中,可通过以下 API 将日志信息打印到控制台中

  • console.log

    打印一般的基础日志信息,当要打印的基础日志太多时可使用 console.group 将相关的日志进行分组。

  • console.warn

    打印带有黄色小图标的警告信息。

  • console.error

    打印带有红色小图标的错误信息。

Mou icon

  • console.assert

    当第一个参数为 false 时,才会打印第二个参数的值。

Mou icon

可以根据 JS 条件判断输出不同的日志信息

当需要换到下一行而不是回车的时候,请按 Shift + Enter

Mou icon

与控制台交互

  • JS 表达式计算

    可以在控制台中输入 JS 表达式,然后点击 Enter 键得出结果。当在控制台输入命令时,会弹出相应的智能提示框,可按 Tab 键补全命令。

Mou icon

  • 选择元素

    $(selector): 列出与 selector 匹配的所有元素。

    $$(selector): 列出与 selector 匹配的所有元素组成了数组。

    **$x(path)**:返回的是一个数组,数组中即为与 xpath 匹配的所有元素。

另外还有两种方法与上面类似:

**document.querySelector(“img”)**:会返回 DOM 中匹配的第一个元素(只返回一个元素)。

**document.querySelectorAll(“img”)**:等同于 $$(selector) 。

Mou icon

点击返回的每个元素,则会定位到页面中的 img 元素及 html 中的具体位置。

Sources

格式化代码

Mou icon

Mou icon

以上图片分别为源代码格式化前和格式化后的状态。通过点击源代码底部的 {} 即可将源代码格式化。

Sinppets 代码片段

我们可以在 Sources 标签页左侧的 Sinppets 按钮中创建代码片段用于测试。

以下创建一个 sum.js 的代码片段。在代码区内写完代码后,可以格式化、打断点,右键选择 Save 保存代码。在 sum.js 文件上右键选择 Run 即可运行代码,代码的运行结果会在底部的 Console 输出。

Mou icon

此外,如果是在你自己的项目环境内,Sinppets 代码片段可执行项目内的方法。

比如,可以新建一个 app.js 代码片段,用于调用我们项目环境中 common.js 的某些方法或字段。

Mou icon

断点调试

在代码中打断点

Mou icon

在源代码左侧处点击即可打上一个断点。 当代码运行到断点处,右侧功能区域会展示出调试的相关信息。右侧最上面一排分别是:暂停/继续、单步执行、单步跳入此执行块、单步跳出此执行块、禁用/启用所有断点。

在调试过程中,把鼠标停留在字段上即可实时看到该字段的值。

快速进入调试的方法

当我们的代码执行到某个程序块方法处,可能你并没有在该方法上设置相关的断点,此时你可以 F11 进入该方法中,但是我们的项目往往都是经过很多源代码封装好的方法,有时候进入后,会走很多底层的封装方法,需要很多步骤才能真正进入这个方法内,此时将鼠标放在此方法上,会出现相关提示,会告诉你在该文件的哪一行代码处,点击即可直接看到这个方法,然后临时打上断点,按 F10 或者点击右上角的第二个按钮即可直接进入此方法的断点处。

调试的功能区域介绍

调试的功能区域在调试页面的右侧。下图为在某方法处打了一个断点。

Mou icon

  • watch

    可以用来实时监视变量的值。

  • Call Stack

    断点执行到程序块停下来后,Call Stack 会显示断点所在处的方法调用栈。

    如果想重新从 Call Stack 调用栈中调用某个方法,可以在 Call Stack 调用栈中的该方法处右键点击 Restart Frame ,那么断点就会跳转到该方法的开头处重新执行。

  • Scope

    可以查看此时局部变量和全局变量的值。

  • Breakpoints

    展示当前所有 js 断点,通过点击按钮可以去掉或加上断点,点击下方的代码表达式可跳转到该断点的程序代码处。

  • XHR Breakpoints

    点击右侧 + 号,可以添加请求的 URL ,当 XHR 调用触发时就会在 request.send() 处中断。

  • DOM Breakpoints

    当给 DOM 元素设置断点(来查看元素的变化情况)时,该 DOM 断点就会出现在 DOM Breakpoints 中。

  • Event listener Breakpoints

    此处列出了各种可能的事件类型。勾选对应的事件类型,当触发该事件类型的 js 代码时就会自动中断。

参考资料:

Chrome开发者工具详解(1)-Elements、Console、Sources面板

超完整的 Chrome 浏览器客户端调试大全

代码逻辑相关

遍历一个List集合

bad:

1
2
3
4
5
6
List<User> userList = new ArrayList<>();
for (int i = 0; i < userList.size(); i++) {
User user = new User();
//省略 n 行代码...
userList.add(user);
}

better:

1
2
3
4
5
6
7
List<User> userList = new ArrayList<>();
User user = null;
for (int i = 0, size = userList.size(); i < size; i++) {
user = new User();
//省略 n 行代码...
userList.add(user);
}

①、第二种方式把 userList.size() 用变量 size 存值避免了每次迭代都去调用该方法(不要在 for 循环的第二个表达式的判 断调用对象的方法或字段);②、把 user 变量的声明放到循环外边,避免每次使用都去声明一下 User 对象。 ③、getCount() 方法的处理也类似,如用 int count = xxx.getCount() 缓存起来。

遍历HashMap的最佳方法

1
2
3
4
5
6
7
8
Map<String, User> userMap = new HashMap<>();
Iterator it = userMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
System.out.println(entry.getKey() + " = " + entry.getValue());
//迭代器方法遍历过程中可以通过 it.remove(); 删除当前遍历的元素
it.remove(); // 避免抛 ConcurrentModificationException 异常
}

字符串

判断字符串str是否为null或空串

bad:

1
2
if (null == str || "".equals(str)) {
}

better:

1
2
if (TextUtils.isEmpty(str)) {
}

TextUtils 是一个非常好用的工具类,把 List 转成字符串,逗号分隔;逗号分隔的 String 字符串,切割成 List ,分别可以 用 TextUtils 的 join 和 split 方法。

容易报空指针的情况

判断一个字符串的内容是否为某值

bad:

1
2
if (str.equals("hello")) {
}

better:

1
2
3
// 避免空指针异常,应该把常量写在前面
if ("hello".equals(str)) {
}

判断集合某个元素对象的某个字段是否为空

bad:

1
2
3
// 如果元素对象为 null 这里就挂了吧
if (null != userList.get(i).name) {
}

better:

1
2
3
// 使用对象的方法或字段时,考虑下对象本身是否可能为 null
if (null != userList.get(i) && null != userList.get(i).name) {
}

常用资源释放

Cursor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
cursor = context.getContentResolver().query(Uri.parse(PROVIDER_SETTINGFILE), null, null, null, null);
//省略 n 行代码...
} catch (Exception e) {
e.printStackTrace();
} finally {
if(cursor != null){
try {
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

流文件

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
public class Test {

private static final int BUFFER_SIZE = 1024;

public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("origin.txt");
fw = new FileWriter("destination.txt");
char[] buf = new char[BUFFER_SIZE];
int len = 0;
while ((len = fr.read(buf)) != -1) {
fw.write(buf, 0, len);
}
} catch (Exception e) {
throw new RuntimeException("读写失败");
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if (fr != null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

WebView

  • 首先就是使用自己封装的 WebView , 不在 xml 里面声明,而是直接代码 new 个对象,传入 application context 防止 activity 引用滥用。
1
2
webView =  new BridgeWebView(getContext().getApplicationContext());
webFrameLayout.addView(webView, 0);

在使用了这个方式后,基本上 90% 的 webview 内存泄漏的问题便得以解决。

  • 而在 Android 4.4 版本以下,会出现 Android Webview 无法自动释放,如在 Fragment 中,使用 onDetach 来释放 Webview 是比较好的时机。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void onDetach() {
releaseWebViews();
super.onDetach();
}

private void releaseWebViews() {
if(webView != null) {
try {
if(webView.getParent() != null) {
((ViewGroup) webView.getParent()).removeView(webView);
}
webView.destroy();
}catch (IllegalArgumentException e) {
}
RefWatcher refWatcher = FApplication.getRefWatcher();
refWatcher.watch(webView);
webView = null;
}
}

参考文章:

Android WebView: 性能优化不得不说的事

android内存优化之webview

Handler

  • 使用弱引用
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
public class NoLeakActivity extends Activity {

private NoLeakHandler mNoLeakHandler;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mNoLeakHandler = new NoLeakHandler(this);
Message message = Message.obtain();
mNoLeakHandler.sendMessageDelayed(message, 2000);
}

private static class NoLeakHandler extends Handler {

private WeakReference<NoLeakActivity> mActivity;

public NoLeakHandler(NoLeakActivity activity) {
mActivity = new WeakReference<>(activity);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}
  • 及时清除消息
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
public class NoLeakActivity extends Activity {

private Handler mHandler = new Handler();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mHandler.postDelayed(new Runnable() {
@Override
public void run() {
// startMainActivity();
}
}, 2000);
}

@Override
protected void onDestroy() {
// 把所有的消息和回调移除
mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}

@Override
public void onBackPressed() {
// 把所有的消息和回调移除(onDestroy 执行不确定,因此这里需执行一遍)
mHandler.removeCallbacksAndMessages(null);
super.onBackPressed();
}
}

其它

反面判断条件

bad:

1
2
3
4
5
6
7
public void testMethod(ArrayList<User> userList) {
if (null != userList && userList.size() > 0) {
for (int i = 0, size = userList.size(); i < size; i++) {
// ...
}
}
}

better:

1
2
3
4
5
6
7
8
public void testMethod(ArrayList<User> userList) {
if (null == userList || userList.isEmpty()) {
return;
}
for (int i = 0, size = userList.size(); i < size; i++) {
// ...
}
}

很多比较复杂的层级判断都可以从这些判断的反面出发,来降低程序的复杂性,从而提高可读性。

if与return搭配

bad:

1
2
3
4
5
6
7
8
9
10
11
public int testIfElse(String cmd) {
if ("1".equals(cmd)) {
return 1;
} else if ("2".equals(cmd)) {
return 2;
} else if ("3".equals(cmd)) {
return 3;
} else {
return 4;
}
}

better:

1
2
3
4
5
6
7
8
9
10
11
12
public int testIfElse(String cmd) {
if ("1".equals(cmd)) {
return 1;
}
if ("2".equals(cmd)) {
return 2;
}
if ("3".equals(cmd)) {
return 3;
}
return 4;
}

对象序列化

Android 中的序列化官方推荐 Parceble , 其实 Parceble 最好用于内存之间数据的交换,如果要把数据写入硬盘的话,推荐实现 Serializable .

SharedPreferences

SharedPreferences.Editor.commit 这个方法是同步的,一直到把数据同步到 Flash 上面之后才会返回,由于 IO 操作的不可控,尽量使用 apply 方法代替。apply 只在 API Level >= 9 才会支持,需要做兼容。不过,最新的 support v4 包已经为我们做好了处理,使用 SharedPreferencesCompat.EditorCompat.getInstance().apply(editor) 即可。

其它优化

  • 静态变量不要直接或者间接引用 Activity、Service 等。这会使 Activity 以及它所引用的所有对象无法释放,然后,用户操作时间一长,内存就会狂升。

  • Handler 机制有一个特点是不会随着 Activity、Service 的生命周期结束而结束。也就是说,如果你 Post 了一个 Delay 的 Runnable , 然后在 Runnable 执行之前退出了 Activity , Runnable 到时间之后还是要执行的。如果 Runnable 里面包含更新 View 的操作,程序崩溃了。

  • 不少人在子线程中更新 View 时喜欢使用 Context.runOnUiThread , 这个方法有个缺点,就是一但 Context 生命周期结束,比如 Activity 已经销毁时,一调用就会崩溃。

  • Application 的生命周期就是进程的生命周期。只有进程被干掉时,Application 才会销毁。哪怕是没有 Activity、Service 在运行,Application 也会存在。所以,为了减少内存压力,尽量不要在 Application 里面引用大对象、Context 等。

  • Activity 的 onDestroy 方法调用时机是不确定的(有时候离开界面很久之后才会调用 onDestroy 方法),应该避免指望通过 onDestroy 方法去释放与 Activity 相关的资源,否则会导致一些随机 bug .

  • 如果使用 Context.startActivity 启动外部应用,最好做一下异常预防,因为寻找不到对应的应用时,会抛出异常。如果你要打开的是应用内的 Activity , 不防使用显式 Intent , 这样能提高系统搜索目标 Activity 的效率。

  • 如果子类实现 Serializable 接口而父类未实现时,父类不会被序列化,但此时父类必须有个无参构造方法,否则会抛 InvalidClassException 异常。

  • transient 关键字修饰变量可以限制序列化。

  • View.getContext 获取控件上下文,写 RecyclerView 的 Adapter 的时候,为了使用 LayoutInflater , 不用在构造函数中传入一个外部的 context .

UI相关

Space

Space 经常用于组件之间的缝隙,其 draw() 为空,减少了绘制渲染的过程。组件之间的距离使用 Space 会提高了绘制效率,特别是对于动态设置间距会很方便高效。正是因为 draw()为空,对该 view 没有做任务绘制渲染,所以不能对 Space 设置背景色。

tools标签

tools 标签可以很好的帮助开发者实时预览 xml 的效果,并且运行以后 tools 标签的内容不会展示出来。例如:

1
2
3
4
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="这段话只在预览时能看到,运行以后就看不到了" />

ContextCompat

Android 6.0 之后 getResources().getColor() 被废弃了,可用 ContextCompat.getColor(context, R.color.color_name) 替换,ContextCompat 是 v4 包里的,请放心使用,另外还有 getDrawable() 等方法。

最近在做一个 IM 的项目,需要存储大量数据到本地数据库。考虑到同一台手机可能会被多个账号登录使用,为了提升数据库查询的效率,以分库的方式来存储不同账号的数据(使用用户账号来作为数据库名称)。

以存储用户信息为例:

  • 先贴出使用代码:
1
2
mUserDAO = new UserDAO(this, account); // 此处的 account 就是要操作的数据库名称
mUserDAO.insert(new User(account, userName));
  • 以下为三个关键类
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
/**
* 数据库帮助类
*
* @author zch
* @since 2018-01-05
*/
public class DBHelper extends SQLiteOpenHelper {

private static final int DB_VERSION = 1;
public static final String TABLE_NAME = "user";

public DBHelper(Context context, String dbName) {
super(context, dbName, null, DB_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table if not exists " + TABLE_NAME + " (account text primary key , userName text)";
db.execSQL(sql);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "drop table if exists " + TABLE_NAME;
db.execSQL(sql);
onCreate(db);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 用户实体类
*
* @author zch
* @since 2018-01-05
*/
public class User {

public String account; // 用户账号,假设唯一,用它作为数据库名称(dbName)
public String userName;

public User(String account, String userName) {
this.account = account;
this.userName = userName;
}
}
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* 用户数据表相关操作
*
* @author zch
* @since 2018-01-05
*/
public class UserDAO {

private DBHelper mDBHelper;

public UserDAO(Context context, String dbName) {
mDBHelper = new DBHelper(context, dbName);
}

/**
* 插入一条数据
*
* @param user
* @return
*/
public boolean insert(User user) {
SQLiteDatabase db = null;
try {
db = mDBHelper.getWritableDatabase();
db.beginTransaction();
ContentValues values = new ContentValues();
values.put("account", user.account);
values.put("userName", user.userName);
db.insertOrThrow(DBHelper.TABLE_NAME, null, values);
db.setTransactionSuccessful();
return true;
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (null != db) {
try {
db.endTransaction();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}

/**
* 删除一条数据
*
* @param user
* @return
*/
public boolean delete(User user) {
SQLiteDatabase db = null;
try {
db = mDBHelper.getWritableDatabase();
db.beginTransaction();
db.delete(DBHelper.TABLE_NAME, "account = ?", new String[]{user.account});
db.setTransactionSuccessful();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != db) {
try {
db.endTransaction();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}

/**
* 获取所有数据
*
* @return
*/
public List<User> getUserList() {
SQLiteDatabase db = null;
Cursor cursor = null;

try {
db = mDBHelper.getReadableDatabase();
cursor = db.query(DBHelper.TABLE_NAME,
new String[]{"account", "userName"},
null,
null,
null, null, null);

if (cursor.getCount() > 0) {
List<User> userList = new ArrayList<>();
while (cursor.moveToNext()) {
User user = new User(cursor.getString(cursor.getColumnIndex("account")), cursor.getString(cursor.getColumnIndex("userName")));
userList.add(user);
}
return userList;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != cursor) {
try {
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != db) {
try {
db.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
}

二维数组中的查找

题目描述

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

思路

从二维数组右上角(或左下角)开始遍历查找。下面两种解法都是从右上角开始查找的,左下角同理。

Solution 1 : (168 ms , 16792 K)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public boolean Find(int target, int[][] array) {
int rowCount = array.length;
int columnCount = array[0].length;
int tempJ = columnCount - 1;
for (int i = 0; i < rowCount; i++) {
for (int j = tempJ; j >= 0; j--) {
if (target == array[i][j]) {
// System.out.println(i + " " + j);
return true;
}
if (target > array[i][j]) {
break;
}
tempJ = j - 1;
}
}
// System.out.println("not exist .");
return false;
}

Solution 2 : (184 ms , 16708 K)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public boolean Find2(int target, int[][] array) {
int rowCount = array.length;
int columnCount = array[0].length;
int i = 0;
int j = columnCount - 1;
while (i < rowCount && i >= 0 && j < columnCount && j >= 0) {
if (target == array[i][j]) {
// System.out.println(i + " " + j);
return true;
}
if (target > array[i][j]) {
i++;
} else {
j--;
}
}
// System.out.println("not exist .");
return false;
}

变态跳台阶

题目描述

一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级 …… 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

思路 1:

在 n 级台阶中,第一步可能会跳 1 级、2 级 …… 、n 级。

假设 n 级台阶有 f(n) 种跳法。

若第一步跳了 1 级,那么剩下的 n - 1 级会有 f(n - 1) 种跳法;

若第一步跳了 2 级,那么剩下的 n - 2 级会有 f(n - 2) 种跳法;

……

若第一步跳了 n - 1 级,那么剩下的 1 级会有 f(1) 种跳法。

总结起来就是:

f(n) = f(n - 1) + f(n - 2) + …… + f(1)       ①

根据递推知识,不难想到

f(n - 1) = f(n - 2) + f(n - 3) + …… + f(1)       ②

将 ② 代入 ① 得:

f(n) = 2 * f(n - 1)

即 n 级台阶有 2 ^ (n - 1) 种跳法。

Solution 1 : (17 ms , 8624 K)

1
2
3
4
5
6
int JumpFloorII(int target) {
if (target <= 0) {
return 0;
}
return 1 << (target - 1);
}

思路 2:

n 级台阶中,每级台阶只有「跳」与「不跳」两种可能。但是第 n 级台阶必须要跳,否则不算到达终点。那么,最终的跳法为 2^n / 2 = 2^(n - 1) 种。

Insight.io for Github

GitHub 代码浏览插件。提供的功能有:跳到定义处、查找引用、目录树结构、基于符号的代码搜索、跳转到 Insight.io 里面。

Octotree

可以在 GitHub 和 GitLab 左侧显示目录树结构。

GitCodeTree

基于 octotree 的码云文件树插件。

jsonView jsonViewer json formatter 格式化

格式化、着色、美观、易读 … jsonView 处理了其他该类插件中文字符的 bug , 支持 json 和 jsonp 格式。

Adblock

号称最受欢迎的 Chrome 扩展,拥有超过 4000 万用户!屏蔽整个互联网上的广告。

广告终结者

清除网页上的所有广告:浮动广告,购物广告,恶意弹窗,跟踪代码,视频广告。让你浏览网页更快更清爽。

Axure RP Extension for Chrome

原型设计工具。

Google文档、表格及幻灯片的Office编辑扩展程序

使用 Google 文档、表格和幻灯片来查看和编辑 Microsoft Word、Excel 和 PowerPoint 文件。

Clear Cache

Clear your cache and browsing data with a single click of a button .

Convertio : 文件格式转换不再是问题

Convertio , 一个在线解决格式转换的插件,支持超过 2500 种不同的格式转换(归档转换器 、音频转换器、文档转换器、PDF 工具、电子书转换器、演示文稿转换器 、OCR 工具等等),史上最全的格式转换小助手了!无论是音频,文档还是视频,它都能方便快捷的转换到你想要的格式,非常齐全!

Infinity : 自定义新标签页,极速访问页面

使用该插件可以自定义 Chrome 的新标签页来实现快速拨号、邮件提醒、天气预报、笔记功能、待办事项、壁纸、历史记录管理等功能。同时还可以在右上角的 “+” 可以看到各种设置选项,根据自己的喜好来添加或者删除标签页上的项目,也可更改壁纸和搜索栏。偶尔浏览到感兴趣的页面时,看到一个不错的话题,你也可以添加到 infinity 新标签页。相比其他浏览器的标签页功能,它确实功能更丰富,操作更方便。

OneTab : 一键收纳N个标签页,还你一个清爽的浏览器

一次打开太多网页,来回切换容易乱,并且出现电脑卡顿,轻拓展 OneTab 可以派上用场了! 点击扩展栏的 OneTab 图标,它能够将当前窗口所有标签集合为一个标签。不仅让你浏览器瞬间整洁无暇,还能节省 95% 的内存。当你想要恢复浏览某个页面的时候,在列表中点击一个链接,即可重新打开。你也可以通过快捷键 alt + shift + 1 来快速打开。最后,OneTab 还能检查是否有重复链接,并且不会重复添加链接。这种小贴士,一般人我可不告诉她。

Kami : PDF 阅读、批注、OCR

PDF 软件那么多,为什么要用 Kami ? 因为它轻巧好用,还免费!首先,它支持 PDF (pdf) , 文件(DOX),PowerPoint(PPTX) , 常见的图像文件(jpg,jpeg,png,gif),使用 Kami 工具,您可以在 PDF、文档或图像文件上批注、标记和协作。在浏览器中查看文件、与他人共享文件、高亮文本、添加文本、下划线文本、添加评论、实时协作、徒手画、OCR 字符识别 扫描版 PDF 文件(检测扫描文件上的文本)、离线支持。这些操作都免费!PDF 软件有的它都有,既生瑜何生亮呀!

Save to Pocket : 一键保存任何内容,以供离线查看

Pocket - 口袋,顾名思义,它将任何内容(文章、视频、微博等等)可汇聚到一个地方,以便在任何设备上随时查看。最重要的是!可以离线查看!当你发现感兴趣的内容却没有时间看完或者想先收藏再细细品味时,如果你直接关闭了网页,下次可能找不到了。这时候一款可以“临时”保存当前正在阅读的文章或者视频的工具派上用场,你可以将它放入 Pocket , 方便你在手机或者电脑端随时查看。

Fireshot : 网页截图和另存为 PDF

很多用户经常有这个问题:“这个网页很重要,我想整页打印出来怎么办?”,“我如何将整个网页另存为图片?” 诸如此类问题的答案就是一款免费的 Fireshot 插件。 它可以对网页中整个屏幕或者是网页的部分视图进行截图操作,在截图之后用户还可以对当前截图的图片进行编辑,包括添加注释,添加线条等操作。除开截图,它还能打印、另存为 PDF 等功能,小巧实用!

Dark Reader - 护眼扩展程序

黑色主题,适用于任何网站。关爱眼睛,就使用 Dark Reader 进行夜间和日间浏览。这是一个护眼扩展程序,通过实时生成黑色主题,为每一个网站启用夜间模式。Dark Reader 反转明亮的颜色,使其网页内容具有高对比度并且在易于夜间阅读。您可以调整亮度,对比度,应用棕褐色滤镜,黑暗模式,设置字体和忽略的网站列表。

LastPass:密码管理软件

LastPass,全球知名在线密码管理工具之一,采用军事级加密算法,支持自动填充网站用户名和密码,与朋友分享登录信息等实用功能,且在全平台同步免费,无需订阅 Premium,即可在手机、网页、电脑端同步你的所有 LastPass 信息。

二维码(QR 码)生成器

在线的二维码生成器。可以把当前的网页直接生成二维码,进行编辑,还可以把文字生成二维码,这个很重要!

下载+

Chrome 的下载管理在二级菜单里,进去很不方便,装了这个插件就可以直接看和管理,很好用。

快捷扩展管理

Chrome 其实很占内存,尤其当插件装多了以后会卡顿,有了这个以后,可以一键管理所有扩展,快速激活、禁用插件。

购物党

在线的比价工具,网购的时候可以看价格历史记录,以及各大网站的价格对比,也有查快递的快捷方式。

右键搜

Chrome 默认的搜索是谷歌搜索,没有翻墙的童鞋可能用起来不方便,但有这个这个就不用担心了。

Cloudbleed Bookmark Checker:检测书签是否有死链

对于书签收藏的狂魔同志,收藏夹里的网页肯定有很多都无法访问了吧!这款扩展就是来检测书签是否有死链的,因为不怎么常用,所以才三颗星。

Imagus

鼠标指针悬停在链接或缩略图上时直接在当前页面的弹出视图上显示这些图片、HTML5 视频/音频和内容专辑。

WhatRuns

WhatRuns 是一款用于了解网站技术的 Chrome 网站技术分析工具,主要能通过分析网站页面所使用的框架、代码等技术以及页面所使用的样式等方面,让使用者能直观的了解网站的整体技术信息。在安装了这款插件后,使用者可以通过点击 WhatRuns 图标来打开插件窗口,通过该窗口使用者可以轻松了解网站的技术信息。

Reader View:Chrome 也有 Safari 的阅读模式

将网页转换成 Safari 阅读模式的样式,让你更方便舒适的阅读网页文字,当你访问文章网页的时候,扩展程序的按钮会显示在地址栏末端,点击就能轻松享受更好的阅读模式。

为什么你们就是不能加个空格呢

每次看到文章中的英文、数字、中文写在一起,你知道我的内心是什么样吗?
你们能不能在它们之间加个空格呢?!不过自从装上了「为什么你们就是不能加个空格呢?」,插件会自动把网页中所有中文、英文、数字、符号之间插入一个空格,从此告别此痛苦,又能和大家好好玩耍啦。

Postman

相信开发者朋友一定知道这款插件,这是一款强大的 API & HTTP 请求调试工具,它不仅可以调试简单的 HTML、CSS 以及脚本等简单的网页基本信息,这款 Chrome 插件甚至还能发送几乎所有的 HTTP 请求,可谓是 Web 开发者的一大利器。

Picee(基于 Github API 的图床)

具体介绍可以参考基于 Github API 的图床 Chrome 插件开发全纪录

技巧

快速查找

双击 Shift 键。堪比 Alfred 的功能。对输入的内容进行模糊查询,若勾选上 Include none-project items 后,还可以搜索非项目中的内容,如引用的 jar 包中的内容。

Search Action

Ctrl + Shift + A。类似搜索指令的入口。如输入 “Open Recent” 可以查找最近的工程;输入 “hier” 选中 Type Hierarchy ^H 即可以查看某个方法或类的调用栈。

演示模式

在菜单栏 View 选项 最下面可找到几种极为方便的演示模式。通过选择这几种模式可以在连接投影仪时非常方便地全屏显示代码区域。代码区在 mac 下可通过双指缩放进行代码区域的缩放。

显示最近操作、修改

Ctrl + ECtrl + Shift + E 快速显示最近文件操作和文件修改。同时可用 Ctrl + Tab 进行各个界面的切换。

操作记录前进和回退

Ctrl + Alt + Left/Right

移动行

Alt + Shift + 方向键上/方向键下。整体移动也是类似的方法。

交换行

Ctrl + Shift + 方向键上/方向键下

Log 快捷模板

在 onCreate 中输入 logi ,按 Enter 即可生成如下一条 Log 日志。相应地,其它级别的日志也可类似地快速生成。

1
Log.i(TAG, "onCreate: ");

查看大纲

(Fn) + Ctrl + F12 。大纲界面显示方法和成员列表。输入关键字可模糊查询方法和成员。

附加调试

在 ADB 连接手机情况下,点击 attach to debugger 按钮并选择要调试的程序(只能调试 Debug 签名的 App),即可进入调试模式,无需通过 Debug 运行程序。对于大项目,这种方式可以以正常的方式进行程序运行,如果使用 Debug 模式运行会非常卡。

代码折叠

全局折叠、展开:Ctrl + Shift + -Ctrl + Shift + +
局部折叠、展开:Ctrl + -Ctrl + +

在文件系统中打开文件

按住 Ctrl 键并点击打开的代码的 Tab 页。

预览方法定义

Ctrl + Shift + i (mac 为 Command + Y)。在本页面预览方法的定义,无需跳转到方法定义的地方去。

拆分窗口

在编辑区域显示多个编辑界面:Window –> Editor Tabs –> Split vertical \ horizontal

Extract 的妙用

Extract 可以重构 Java 代码;抽取布局 XML 的一些属性作为 Style;抽取布局 Layout。
在代码中,Extract 可提取各种变量、参数、常量。如将一个局部变量提取为类的成员变量,将一个字符串的常量提取为全局的常量(可选择提取到这个类本身或新的类中)。

方法调用栈

Ctrl + Alt + H 可以快速找到该方法的调用栈。

Surround With

Ctrl + Alt + T。可快速对某段代码进行重构,如增加判空的 if 条件、增加 try catch 捕获异常。

Image Asset && Vector Asset

可帮助快速创建不同分辨率的图像和 SVG 文件。要使用该功能,在 res 资源目录下右键选择 New。

断点

  • 条件断点
    满足某个条件时断住。在普通断点上右键,在弹出菜单的 Condition 中填入断点条件即可。如在循环里面需要 i == 5 时使用断点,则在 Condition 输入 i == 5。
  • 临时断点
    执行一次断点后该断点就会消失。在当前行使用快捷键 (Fn) + Ctrl + Alt + F8,即可生成一个临时断点,临时断点上有一个数字“1”。
  • 异常断点
    在 Run 菜单打开 View breakpoints 界面,点击右上角的 “+”,选择 Java Exception Breakpoints,并输入要监听的异常即可。如输入 NullPointerException,则在程序运行时不需设置任何断点,只要 App 因为 NullPointerException 异常而导致崩溃,系统就会在对应的地方自动断点并暂停。
  • 日志断点
    当代码写完了,突然出现一个 bug 需要加一行 Log 进行调试,但又不想为了这一行 Log 而重新编译一遍整个工程。此时可以使用日志断点解决这个问题。首先,在需要断点的地方打上一个普通断点,然后在断点处右键,选择 suspend 属性为 false,并在下方的 Log evaluated expression 中写入日志信息即可。如此设置后,在程序运行时则无需重新编译即可在断点处打出日志信息。

代码模板

内置模板

Ctrl + J 调出代码模板。这些模板在设置的 Live Templates 标签中。这里不仅提供了 Java 代码的快捷模板,连 Android 注释、Log、甚至 XML 都有非常多的快捷模板。

后缀模板

Ctrl + J 调出代码模板后。如需遍历一个 List 类型的变量 list,只需输入 list.for 快速生成遍历模板、输入 list.cast 快速生成类型转换模板。

自定义模板

方法注释

①、打开设置,选择 “Live Templates”;②、点击右栏的加号,选择增加一个 Template Group,并在该 Group 下新增一个 Template;③、选中自定义的注释模板,如 ma ,在下方的编辑区域中进行注释代码的编辑,如下代码模板;④、经过这样的配置后,在方法前输入 “ma” 即可弹出该模板,按 Enter 键后确认输入。

1
2
3
4
5
6
/**
* $desc$
*
* @author zch
* create at $date$
*/

文件、类注释

①、打开设置,选择 “File and Code Templates”;②、选择 Includes 标签,创建名称为 “ClassHeader” 的模板和名称为 “FileHeader” 的模板;③、有了这两个模板就可以组合这些模板来创建新的完整类、文件模板。如在 Files 标签中新建一个名称为 “MyActivity” 的模板文件,并设置代码模板。④、新建文件的时候选择 “MyActivity” 即可创建该种模板的文件。

ClassHeader 模板:

1
2
3
4
5
6
/**
* class description here
* @author ${USER}
* @version 1.0.0
* @since ${YEAR}-${MONTH}-${DAY}
*/

FileHeader模板:

1
2
3
4
5
6
/*
* ${NAME} ${YEAR}-${MONTH}-${DAY}
* Copyright (c) ${YEAR} jufuns. All right reserved.
*
*/
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end

MyActivity模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#parse("FileHeader.java")

import android.app.Activity;
import android.os.Bundle;

#parse("ClassHeader.java")
public class ${NAME} extends Activity{

@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}

}

用 MyActivity 模板新建的 LoginActivity:

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
/*
* LoginActivity 2016-10-07
* Copyright (c) 2016 jufuns. All right reserved.
*
*/
package com.jiejue.catwalk.ui.ac;

import android.app.Activity;
import android.os.Bundle;

/**
* class description here
*
* @author zch
* @version 1.0.0
* @since 2016-10-07
*/
public class LoginActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

}

}

类似地,我们也可以建立 Adapter、单例等等的模板代码。

立即停止 AndroidStudio 编译

一个命令就可以停止它!

只需进入项目文件夹(在 AS 的 terminal 窗口),然后输入以下命令即可。

1
2
3
4
5
mac
./gradlew --stop

window
gradlew --stop

就是这么简单,这个命令会杀死编译的守护进程,编译会立即停止。

插件

.ignore

给 Git 项目生成最合适的 ignore 文件。

ButterKnife Zelezny

在代码中的布局文件单击鼠标右键,选择 Generate-Generate ButterKnife 即可自动生成 ButterKnife 所需的注解文件。

SelectorChapek

可将一个 drawable 文件夹下的图像自动生成对应的 drawable selector,只要文件名符合安装要求的规范即可。

GsonFormat

可将一段 Json 生成所需的 Gson 实体。

Android Parcelable code generator

可自动生成 Parcelable 接口所需的代码。

AndroidCodeGenerator

可在 getView 方法中根据布局文件的 ID,快速生成对于的 ViewHolder。

Prettify

可根据 Layout 自动生成该 Layout 中的 View 在 Java 中的 findViewById 代码。

Exynap

Exynap 一个帮助开发者自动生成样板代码的 AndroidStudio 插件。

Android Methods Count

高效统计 Android 开源库的方法数。

AndroidLocalizationer

可用于将项目中的 string 资源自动翻译为其他语言的 Android Studio/IntelliJ IDEA 插件。

Key Promoter

当用鼠标点击 AS 的一个功能时,Key Promoter 插件会展示该功能的快捷键。

FindBugs-IDEA

一个免费的 Android Studio 插件,可以在开发早期检测出常见的 Java bug .

ADB Idea

ADB Idea 一个开源的 Android Studio 插件,帮助你在 IDE 中实现 app 重启,杀死,清理数据,卸载。

Codota

Codota 写代码经常会遇到需要从 github 或者 stackoverflow 上寻找代码示例的时候,这个插件可以在无需离开 IDE 就能做这件事情。

工具

Stetho

Stetho 是 Facebook 开发的 Android 调试工具。它可以通过 Chrome 的开发者工具来辅助 Android 开发。提供有网络抓包、查看本地数据(比如 Sqlite 数据库,Sharepreference 等等)、Javascript 控制台、View Hierarchy 布局层级查看、Dump App 等功能。

Gradle, please

Gradle, please 帮助快速地找到第三方库 gradle 依赖的那行代码。比如我们要使用 glide , 只需在一个输入框中输入 glide , 下面就会显示 glide 的完整依赖。当我们搞不清楚库的拼写或者版本号这些细节时,就显得相当有用。

LeakCanary

LeakCanary 是由 Square 开发的一个开源工具,让复杂的内存泄漏检测变得更简单。它可以在内存泄漏的时候显示通知,并提供一个完整的泄漏轨迹。

Android Debug Database

Android Debug Database 是一个非常酷的开源工具,完全改变了 debug 数据库和 shared preferences 的方式。现在你可以在一个漂亮的界面上查看,编辑,删除数据,以及运行 sql 语句。

Android WiFi ADB

Android WiFi ADB 可以通过 Wi-Fi 从 Android Studio 运行 app . 无需用数据线把设备和电脑连接起来。

drawable-optimizer

drawable-optimizer 一个通过优化 PNG 图片来减小 APK 体积的 gradle 插件。

DevKnox

app 中会有一些难以意识到的安全漏洞,要杜绝这些漏洞往往需要相当的经验和精力。但是这个工具可以帮助你检测安全漏洞,就像使用拼写检查一下简单。使用方法:选中要查找问题的 java 文件或者文件夹,右键 Devknox Scan -> Devknox Scan 就会开始扫描。

ClassyShark

ClassyShark 可以帮助你窥探任何 apk 获得许多有用的信息,比如 classes , resources , manifest , dependencies , dex count 等等。它可以让你了解一个 app 是做什么的甚至是如何做到的。ClassyShark 是开源的。

0%