从 CSDN 博客迁移到个人 Hexo 站点的一点坑

前言

最近把 原来 CSDN 博客迁移到了个人博客。

原先 CSDN 博客:http://blog.csdn.net/never_cxb

独立个人博客:http://www.alijava.com/

配置信息

  • 由 Hexo 强力驱动
  • 主题 - NexT.Mist
  • 图片存储于七牛云
  • 服务器 nginx
  • 部署于阿里云
  • 已备案
  • 评论: 多说
  • 统计阅读次数:LeanCloud
  • 搜索引擎优化:百度站长

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

nginx 常用命令

  • sudo service nginx restart 重启服务器nginx
1
2
3
sudo service nginx start
sudo service nginx stop
sudo service nginx restart

安装 next 官网教程配置腾讯404 页面,发现总是无法显示

后来发现是因为nginx的问题,需要配置 404路径,

设 404 为 hexopublic 目录 /usr/local/hexo/blog/public

加上 fastcgi_intercept_errors on,现在就可以正常访问了

http://www.alijava.com/404.html

1
2
3
4
5
6
7
error_page 404 /404.html;
location = /404.html {
root /usr/local/hexo/blog/public;
}
fastcgi_intercept_errors on;

Todo List

  • git 版本控制,备份
  • 推广,搜索引擎优化

Hexo 常用命令

  • hexo new "java-basics" 或者 hexo n "java-basics"

  • hexo generate 或者 hexo g 生成静态文件至public目录

  • hexo server 或者 hexo s 启动服务预览

  • hexo s -p 5000 将默认端口4000修改为5000

保持ssh连接不断线

1
ssh -o ServerAliveInterval=60 root@115.**.**.**

意思是每60秒发送一个信号重连

搜索引擎优化

主要是通过百度站长平台,希望能够提升博客在搜索引擎的收录数量及排序位置

主动推送(实时)

官方文档这么说:

将要提交的链接按照每行一条的格式写入一个文本文件中,命名此文件为urls.txt,
然后进入该文件所在目录,执行如下命令:

1
curl -H 'Content-Type:text/plain' --data-binary @urls.txt "http://data.zz.baidu.com/urls?site=www.alijava.com&token=2FncXBwaZ5iVh5oT"

为了方便提交,写了一个脚本,方便 curl 提交链接。

(1)ls source/_posts | grep ".md$" ,输出source/_posts.md结尾的文件名
(2)笔者的文章 urlhttp://www.alijava.com/ + 文件名,利用 sed -e 's/\.md$//'去掉后缀 .md
(3)sed -e 's/^/http:\/\/www.alijava.com\//'加上网站前缀

1
2
ls source/_posts | grep ".md$" | sed -e 's/\.md$//' -e 's/^/http:\/\/www.alijava.com\//' > urls.txt
echo `curl -H 'Content-Type:text/plain' --data-binary @urls.txt "http://data.zz.baidu.com/urls?site=www.alijava.com&token=你的token"`

sitemap

注意在你的hexo 博客目录,执行下面命令

1
2
npm install hexo-generator-sitemap --save
npm install hexo-generator-baidu-sitemap --save

站点配置文件 增加

1
2
3
4
sitemap:
path: sitemap.xml
baidusitemap:
path: baidusitemap.xml

然后重新编译并部署

1
2
hexo clean
hexo g

站点地图就可以通过下面链接访问了

sitemap 的坑

把生成的sitemap提交百度站长之家后,发现状态为主域校验失败

原因是生成的 sitemap.xml 里面的是 http://yoursite.com/common-tools/,前缀不是网站真实网址

1
2
3
4
<url>
<loc>http://yoursite.com/common-tools/</loc>
<lastmod>2016-09-03T08:28:25.843Z</lastmod>
</url>

目前的方法是手动修改 sitemap.xmlbaidusitemap.xml,但是每次hexo g部署之后都需要修改,不知道有没有其他方法

可以利用 vim 修改文件

1
:%s/yoursite/www.alijava/g

yoursite.com替换为www.alijava.comg表示替换所有

参考文章

2017互联网校园招聘的一些面试题

前言

参加了2017年校招,面试了阿里、百度、腾讯、滴滴、美团、网易、去哪儿等公司,个人是客户端 Android 方向,总结了面试过程中频率出现较高的题目,希望对大家有所帮助。

Java 一些知识点

Object 有哪些方法

  • public 方法:getClassequalshashCodetoStringwaitnotify
  • protected 方法:clonefinalize
  • private 方法:registerNatives,该方法作用是将不同平台C/C++实现的方法映射到Java中的native方法
1
2
3
4
5
6
7
public class Object {
private static native void registerNatives();
// 声明后有紧接静态代码块
static {
registerNatives();
}
}

自动装箱

1
2
3
4
5
6
public static void main(String[] args) {
int i = 0;
Integer j = new Integer(0);
System.out.println(j == i);
System.out.println(j.equals(i));
}

上述代码的输出是

1
2
true
true

Java 虚拟机 GC 根节点的选择

Java通过可达性分析来判断对象是否存活。基本思想是通过一系列称为”GC roots”的对象作为起始点,可以作为根节点的是:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象

笔者这么理解,作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中。
虚拟机栈、本地方法栈这都是局部变量,某个方法执行完,某些局部使用的对象可以被回收。

类加载机制

  • 启动类加载器( Bootstrap ClassLoader)

    启动类加载器无法被 java 程序员直接引用, 这个类加载器负责把存放在<JAVA_HOME>\lib目录中的, 或者被-Xbootclasspath参数指定路径中的, 并且是被虚拟机识别的类库加载到虚拟机内存中.

  • 扩展类加载器(Extension ClassLoader)

    负责加载在<JAVA_HOME>\lib\ext目录中的, 或者被java.ext.dirs系统变量所指定的路径中的所有类库。

  • 应用程序类加载器( Application ClassLoader )

    这个类加载器是ClassLoader 中的 getSystemClassLoader()方法的返回值, 一般称其为系统类加载器, 它负责加载用户类路径( ClassPath )上所指定的类库

从 java 虚拟机的角度而降, 只存在两种不同的类加载器:

  • 一个是启动类加载器( Bootstrap ClassLoader ), 这个类加载使用 C++ 语言实现, 是虚拟机自身的一部分;

  • 另一种是其他所有的类加载器, 他们由 java 语言实现, 独立于虚拟机之外, 并且全部继承自java.lang.ClassLoader

加载类的寻找范围就是 JVM 默认路径加上Classpath, 类具体是使用哪个类加载器不确定。

类加载主要步骤

  • 加载 把 class 文件的二进制字节流加载到 jvm 里面
  • 验证 确保 class 文件的字节流包含的信息符合当前 jvm 的要求 有文件格式验证, 元数据验证, 字节码验证, 符号引用验证等
  • 准备 正式为类变量分配内存并设置类变量初始值的阶段, 初始化为各数据类型的零值
  • 解析 把常量值内的符号引用替换为直接引用的过程
  • 初始化 执行类构造器<clinit>()方法
  • 使用 根据相应的业务逻辑代码使用该类
  • 卸载 类从方法区移除

双亲委派模型

除了顶层的启动类加载器之外, 其余的类加载器都应当有自己的父类加载器, 父子关系这儿一般都是以组合来实现。

工作过程: 如果一个类加载器收到了类加载的请求, 它首先不会自己去尝试加载这个类, 而是把这个请求委派给父类加载器去完成, 最终所有的加载请求都会传送到顶层的启动类加载器中, 只有当父类加载器反馈自己无法完成这个请求时候, 才由子加载器来加载。

例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。

对于任何一个类, 都需要由加载它的类加载器和这个类本身一同确定其在 java 虚拟机中的唯一性。

ClassLoader.loadClass()的代码如下,先检查是否已经被加载过,如果没有则parent.loadClass()调用父加载器的loadClass()方法,如果父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载器加载失败,抛出ClassNotFoundException,再调用自己的findClass()方法进行加载。

另外,如果我们自己实现类加载器,一般是Override复写 findClass方法,而不是loadClass方法。

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
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name); //可以Override该方法
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

Java 后台的一点知识

JSP 与 Servlet 的关系

  • Tomcat 等 Web 容器最终会把 JSP转化为 Servlet
  • Jsp更擅长表现于页面显示, Servlet更擅长于逻辑控制
  • Servlet是利用 System.out.println()来输出 html 代码,由于包括大量的HTML标签、大量的静态文本及格式等,导致Servlet的开发效率低下
  • JSP通过在标准的HTML页面中嵌入Java代码,其静态的部分无须Java程序控制,Java 代码只控制那些动态生成的信息
  • 最终 JSP 被容器解释为 Servlet,其中Html 代码也是用 System.out.println()等拼接输出的
  • JSP 第一次访问的时候,要转化为 java 文件,然后编译为 class 文件,所以第一次访问 JSP 速度会比较慢,后面会快很多

Servlet 生命周期

主要是java.servlet.Servlet接口中的init()service() 、和destroy() 3个方法。

  • 初始化阶段,web容器通过调用init()方法来初始化Servlet实例,在Servlet的整个生命周期类,init()方法只被调用一次
  • 客户请求到来时,容器会开始一个新线程,并调用servlet的 service()方法,service() 方法根据请求的http方法来调用 doget()dopost()
  • 终止阶段调用destroy()方法,销毁一些资源

GET 请求 vs POST 请求

  • GET用于信息获取,是安全的和幂等的,GET一般是对后台数据库的信息进行查询
  • POST表示可能修改变服务器上的资源的请求,一般是对后台数据库进行增、删、改的操作
  • GET请求的参数会跟在URL后进行传递,请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,一般浏览器对 URL 的长度会有限制
  • POST请求,提交的数据则放置在是HTTP包的包体中,用类似Key-Value的格式发送一些数据,相对来说,GET请求会把请求的参数暴露在 URL 中,安全性比POST差一些

HTTP 请求的基本格式

  • <request line> 请求行
  • <headers> 请求头(参数头)
  • <blank line> 空白行
  • [<request-body>] 请求实体(GET没有, POST有

数据库

索引的分类

主要分为聚集索引和非聚集索引:

  • 聚集索引存储记录物理上连续,而非聚集索引是逻辑上的连续,物理存储并不连续
  • 聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个

ResultSet 统计记录数目

Java 中使用JDBC连接数据库,最后都会得到一个 ResultSet,比如如下的代码

1
2
3
4
Connection con = DriverManager.getConnection(url, username, password);
Statement sta = con.createStatement();
String sql = "select * from student";
ResultSet resultSet = sta.executeQuery(sql);

那么如何根据得到的ResultSet统计一共有多少条记录呢?注意:ResultSet没有提供类似size()length的 API 来直接获取总记录数。

方法1:利用循环

1
2
3
4
int sum = 0;
while(resultSet.next()){
sum++;
}

方法2:利用ResultSet的getRow方法来获得ResultSet的总行数

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
resultSet.last(); //移到最后一行
int rowCount = resultSet.getRow(); //得到当前行号,也就是记录数
```
## 设计模式 ##
### 单例模式 ###
单例模式中必须保证只有一个实例存在。有时候单例是为了避免重复创建多个实例造成资源浪费,有时候也是为了避免多个不同的实例导致系统不一致的行为。
Android 中,App启动时系统会创建一个`Application`对象,用来存储系统的一些信息,这儿的`Application` 就是是单例模式的应用。可以通过`Context.getApplicationContext()`获取唯一的`Application`实例。
```java
class Singleton {
private volatile static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (Singleton.class) {
//第二重判断
if (instance == null) {
instance = new Singleton(); //创建单例实例
}
}
}
return instance;
}
}

为什么synchronized里面需要加一次判断if (instance == null),是考虑这样的特殊情形:比如线程A、B都到达第一个if (instance == null),线程A进入synchronized代码中创建实例,线程B排队等待。但当A执行完毕时,线程B进入synchronized锁定代码,它并不知道实例已经创建,将继续创建新的实例,导致产生多个单例对象。

也可以用内部类的方式创建,

1
2
3
4
5
6
7
8
9
10
public class Singleton(){
private static class Inner {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance(){
return Inner.instance;
}
}

模板方法模式

在父类中实现一个算法不变的部分,并将可变的行为留给子类来实现。

比如AsyncTask 里面的四个方法onPreExecutedoInBackgroundonProgressUpdateonPostExecute

还有Activity也应用了模板方法模式
onCreateonStartonResumeonPauseonStoponDestroyonRestart

适配器模式

分为两种:类的适配器模式、对象的适配器模式

Android 里的 ListViewRecyclerViewsetAdapter()方法就是使用了适配器模式。

观察者模式

在 GUI 中,不管是 Windows 桌面应用、或者 Android、IOS,都会给某个按钮 Button 设置监听事件,这儿就是使用了观察者模式。Android 中设置 Button 的监听事件代码如下:

1
2
3
4
5
6
final Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});

笔试编程题

线程 VS 进程

关于线程和进程,不正确的描述是__。(选 D 栈是线程私有, 保存其运行状态和局部变量 )
A. 进程的隔离性要好于线程
B. 线程在资源消耗上通常要比进程轻量
C. 不同进程间不会共享逻辑地址空间
D. 同一个进程的线程之间共享内存,包括堆和栈
E. 进程间有途径共享大量内存中的数据
F. 线程间通讯可以通过直接访问全局变量,或者使用进程间通讯的机制(IPC)

找出未打卡的员工

题目:输入两行数据,第一行为全部员工的 id,第二行为某一天打卡的员工 id,已知只有一个员工没有打卡,求出未打卡员工的 id。(员工 id 不重复,每行输入的 id 未排序)
输入:
1001 1003 1002 1005 1004
1002 1003 1001 1004
输出:
1005

分析:可以用两个 List,第一个 List 保存所有员工的 id,第二个 List 保存打卡员工的 id,从第一个List 中把第二个 List 的数据都删除,最终剩下的就是未打卡员工的 id。

更好的方法:异或,两行数据中未打卡员工的 id 出现了一次,其余员工的 id 都出现了2次,两个相同的数异或为0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
String[] ids = scan.nextLine().split(" ");
String[] marks = scan.nextLine().split(" ");
int result = 0;
for (int i = 0; i < ids.length; i++) {
result ^= Integer.parseInt(ids[i]);
}
for (int i = 0; i < marks.length; i++) {
result ^= Integer.parseInt(marks[i]);
}
System.out.println(result);
}
}

手写代码题

快速排序

排序是经典面试题,公司也希望通过手写快排来考察面试者的编程习惯和基本功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 排序范围 [start, end], 包含 end
public void sort(int[] arr, int start, int end) {
if (start < end) {
int p = partition(arr, start, end);
quickSort(arr, start, p - 1);
quickSort(arr, p + 1, end);
}
}
// 一次划分代码,返回被划分后的基准位置
public static int partition(int[] arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (left < right && arr[right] >= pivot)
right--;
if (left < right)
arr[left++] = arr[right];
while (left < right && arr[left] <= pivot)
left++;
if (left < right)
arr[right--] = arr[left];
}
arr[left] = pivot;
return left;
}

Note:快排是不稳定的,常见的稳定排序是:冒泡、插入、归并

括号字符串是否合法

某个字符串只包括(,判断其中的括号是否匹配正确,比如(()())正确,((())()错误,不允许使用栈

这种类似题的常见思路是栈,对于左括号入栈,如果遇到右括号,判断此时栈顶是不是左括号,是则将其出栈,不是则该括号序列不合法。

面试官要求不能使用栈,可以使用计数器,利用int count字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static boolean checkBrackets(String str) {
char[] cs = str.toCharArray();
int count = 0;
for (int i = 0; i < cs.length; i++) {
if (cs[i] == '(')
count++;
else {
count--;
if (count < 0) {
return false;
}
}
}
return count == 0;
}

扑克牌随机发牌

对于52张牌,实现一个随机打算扑克牌顺序的程序。52张牌使用 int 数组模拟。

该算法的难点是如何保证随机性?有个经典算法shuffle,思路就是遍历数组,在剩下的元素里再随机取一个元素,然后再在剩下的元素里再随机取一个元素。每次取完元素后,我们就不会让这个元素参与下一次的选取。

1
2
3
4
To shuffle an array a of n elements (indices 0..n-1):
for i from n − 1 downto 1 do
j ← random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]

注意这儿是0 ≤ j ≤ i,包括j=i的情况,因为可能洗牌后某个牌未发生交换,比如第51张牌还是原来的第51张牌。

1
2
3
4
5
6
7
8
9
10
11
public void randomCards() {
int[] data = new int[52];
Random random= new Random();
for (int i = 0; i < data.length; i++)
data[i] = i;
for (int i = data.length - 1; i > 0; i--) {
int temp = random.nextInt(i+1); //产生 [0,i] 之间的随机数
swap(data,i,temp);
}
}

智力题

金条付费

你让工人为你工作7天,回报是一根金条,这个金条平分成相连的7段,你必须在每天结束的时候给他们一段金条,如果只允许你两次把金条弄断,你如何给你的工人付费?

答案:切成一段,两段,和四段.

第1天: 给出1.
第2天: 给出2,还回1.
第3天: 给出1.
第4天: 给出4,还回1+2.
第5天: 给出1.
第6天: 给出2,还回1.
第7天: 给出1.

赛马

25匹马,速度都不同,但每匹马的速度都是定值。现在只有5条赛道,无法计时,即每赛一场最多只能知道5匹马的相对快慢。问最少赛几场可以找出25匹马中速度最快的前3名?

答案:

  • 25匹马分成5组,先进行5场比赛
  • 再将刚才5场的冠军进行第6场比赛,得到第一名。按照第6场比赛的名词把前面5场比赛所在的组命名为 A、B、C、D、E 组,即 A 组的冠军是第6场第一名,B 组的冠军是第二名 …
  • 分析第2名和第3名的可能性,如果确定有多于3匹马比某匹马快,那它可以被淘汰了。因为 D 组是第6场的第四名,整个D 组被淘汰了,同意整个 E 组被淘汰。剩下可能是整体的第2、3名的就是C组的第1名、B组的1、2名、A组的第2、3名。取这5匹马进行第7场比赛
    -所以,一共需要7场比赛

本文链接:http://www.codeceo.com/article/it-interview-question-2017.html

javac、javap命令行执行 java 程序的一点小坑

前言

2016校园招聘季,正在复习 java,想使用 javacjavajavap等命令分析String s = "a" + "b" + "c" + "d" 会创建几个对象,没想到执行java 命令总是报错。

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
TomChens-MBP:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MBP:util tomchen$ ls
Main.java
TomChens-MBP:util tomchen$ cat Main.java
package util; //包名
public class Main {
public static void main(String[] args) {
String s = "a" + "b" + "c" + "d";
System.out.println(s);
}
}

Main.java 是在 Eclipse 工程下的util包里(注意就是这个 util包的影响!!)

javac

在目录src/util 执行 javac,没有问题

1
2
3
TomChens-MBP:util tomchen$ javac Main.java
TomChens-MBP:util tomchen$ ls
Main.class Main.java
  • javac 命令的使用方式是 javac [文件名(带后缀名 .java)]
  • java 命令是 java [文件名(不带后缀名)]

javap 字节码反编译

依然在目录src/util下执行javap

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
TomChens-MBP:util tomchen$ javap -c -v Main.class
Classfile /Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util/Main.class
Last modified Oct 3, 2016; size 421 bytes
MD5 checksum 0700e51efd4656898d4c1c136394661f
Compiled from "Main.java"
public class util.Main
SourceFile: "Main.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = String #16 // abcd
#3 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // util/Main
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Main.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Utf8 abcd
#17 = Class #23 // java/lang/System
#18 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 util/Main
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public util.Main();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #2 // String abcd
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 10
}

反编译主要是分析上面的String s = "a" + "b" + "c" + "d";

由上面的#2 = String #16 // abcd
可知:"a" + "b" + "c" + "d"只创建了一个对象,因为"a""b""c""d"等都是字符串常量,对于常量,在编译时候直接存储这几个常量拼接后的结果。

java

用惯了 Eclipse,想试下命令行输出,没想到执行java命令却老是报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TomChens-MBP:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MBP:util tomchen$ java Main
Exception in thread "main" java.lang.NoClassDefFoundError: Main (wrong name: util/Main)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

报了一堆问题,看网上的答案说可能是 CLASSPATH 的问题。

试验 TomChens-MBP:util tomchen$ java -classpath . Main还是不行。

怀疑可能是环境变量的问题,mac 的环境变量和 windows 有区别。

Mac 设置 JAVA_HOME 和 PATH

设置 JAVA_HOME,网上找到一个方法,该方法的优点是没有把JAVA_HOME写死:

1
2
3
4
5
// 编辑文件'~/.zshrc'或'~/.bash_profile',加入下面两行:
// 对于Java 1.7:
export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
export PATH=${JAVA_HOME}/bin:$PATH

对于 CLASSPATH ,网上大多意见是:不要设置全局的环境变量,每个应用会自己设置。有点疑惑?

  • Mac OS X (Unix/Linux)中,路径分隔符是冒号(:)
  • Windows是分号(;)

但是设置了环境变量还是没能解决上述的问题。

package 的原因

后来看到一篇文章,发现要在 src 目录下执行 java 命令,而不是在/src/util 下执行。

1
2
3
4
5
6
7
TomChens-MBP:util tomchen$ cd ..
TomChens-MBP:src tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src
TomChens-MBP:src tomchen$ java util.Main
abcd
TomChens-MBP:src tomchen$ java util/Main
abcd

可以看到,用util.Main或者util/Main指定包名都可以。

如果不是在src执行,而是在 src/util执行java util/Main,还是会报错

1
2
3
4
TomChens-MacBook-Pro:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MacBook-Pro:util tomchen$ java util/Main
Error: Could not find or load main class util.Main

总结与思考

javac 命令与 java 命令的区别

  • javac 命令,在 src下执行javac util/Main.java可以,在src/util下执行javac Main.java也可以。
  • 执行 java 命令需在 包的上层目录(比如包util的上层目录: src),并且类名前面要带上包名。因为Java存在类名相同但包名不同的情况,比如 java.awt.Listjava.util.List,包名+类名才能唯一识别某个类。
  • javac是一个平台命令,它对具体的平台文件进行操作,要指明被编译的文件路径
  • java是一个虚拟机命令,它对类操作,即对类的描述要用点分的描述形式,并且不能加扩展名,还要注意类名的大小写。

参考文章

经典排序算法总结--冒泡、快排、插入、希尔、归并、选择

前言

互联网面试,排序是经典问题,总结几种经典排序代码,方便后期查阅。

本文链接 http://www.alijava.com/sort-summary/ 转载请注明出处。

如何测试编写的排序代码? 可以利用在线编程OJ系统 ,比如牛客网的
http://www.nowcoder.com/questionTerminal/508f66c6c93d4191ab25151066cb50ef

冒泡排序

对纵向排列的关键字序列,按照自下而上的扫描方向对两两相邻的关键字进行比较,
若为逆序(k_j < k_j-1 ),则将两个记录交换位置;
重复上述扫描排序过程,直至没有记录需要交换为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void bubbleSort(int[] arr, int size) {
boolean swap = false;
for (int i = 0; i < size - 1; i++) { //最多进行 n-1 趟
swap = false;
for (int j = size - 1; j > i; j--) { //从下往上扫描
if (arr[j] < arr[j - 1]) {
swap(arr, j, j - 1);
swap = true;
}
}
if (!swap) {
break; // 未发生交换,终止算法
}
}
}

冒泡排序的优化:
至多需要 n-1 趟扫描,如果在某趟扫描后,待排序记录已是有序,可以在此趟扫描后终止。
可引入布尔量swap,每次扫描前值为false,若排序过程中发生了交换,置为true
在一趟扫描后,如果swap仍为false,表示本次未曾交换记录,可以终止算法。

交换函数

1
2
3
4
5
public static void swap(int[] arr, int one, int two) {
int temp = arr[one];
arr[one] = arr[two];
arr[two] = temp;
}

快速排序

通过一趟排序将记录序列分成两个子序列,
再分别对这两个子序列进行排序以达到整个序列有序。

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
// 排序范围 [start, end], 包含 end
public static void quickSort(int[] arr, int start, int end) {
if (start < end) {
int p = partition(arr, start, end);
quickSort(arr, start, p - 1);
quickSort(arr, p + 1, end);
}
}
// 一次划分代码,返回被划分后的基准位置
public static int partition(int[] arr, int left, int right) {
int pivot = arr[left];
while (left < right) {
while (left < right && arr[right] >= pivot)
right--;
if (left < right)
arr[left++] = arr[right];
while (left < right && arr[left] <= pivot)
left++;
if (left < right)
arr[right--] = arr[left];
}
arr[left] = pivot;
return left;
}

快速排序的优化:
基准的选择影响快速排序的性能,最理想的情况是:选择的基准恰好能把待排序序列分成两个等长的子序列。

上文选择基准是固定使用序列的第1个元素,改进思路是:使用左端、右端和中间位置上的三个元素的中位数作为基准。

非递归实现 快速排序

思路就是用模拟递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void quickSort(int[] arr, int start, int end) {
int[] stack = new int[end - start + 1]; // 数组模拟栈
int len = 0;
stack[len++] = start; // 入栈
stack[len++] = end;
while (len > 0) { // 栈中还有元素
int right = stack[--len]; // 注意是 --len
int left = stack[--len];
int p = partition(arr, left, right);
if (p - 1 > left) {
stack[len++] = left;
stack[len++] = p - 1;
}
if (p + 1 < right) {
stack[len++] = p + 1;
stack[len++] = right;
}
}
}

插入排序

把关键字k_i依次与有序区的关键字k_i-1k_i-2、··· 、k_1比较
找到应该插入的位置,将k_i插入,后面的序列要往后移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void insertSort(int[] arr, int size) {
int temp = arr[0];
for (int i = 1; i < size; i++) {
// 若 arr[i] 不小于有序区的最后一个元素,直接扩大有序区
if (arr[i] < arr[i - 1]) {
temp = arr[i];
int j = i - 1;
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j--];
}
arr[j + 1] = temp;
}
}
}

希尔排序

插入排序当 n 较小时效率较高;当一组记录有序时,插入排序的算法复杂度可达到最优,即 O(n)。
希尔排序正是基于这两点对插入排序进行改进的。

希尔排序的基本思想:设置 t 个整数增量:d_1d_2、···、d_t,其中d_1 < n, d_t=1
d_1为增量,将所有距离为d_1的记录放到同一个组,可以得到d_1个组,在各组内进行直接插入排序;
然后取第二个增量d_2,重复上述的分组和排序,直至增量d_t=1

设置增量序列时,要使得增量值没有除 1 之外的公因子,最后一个增量值必须为 1。
希尔排序的时间复杂度取决于增量序列的选取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// shellSort(origins, origins.length, new int[] { 5, 3, 1 });
public static void shellSort(int[] arr, int size, int[] d) {
for (int k = 0; k < d.length; k++) {
int gap = d[k];
for (int j = 0; j < gap; j++) { // 对于增量值 gap,一共 gap 组,0~gap-1
for (int i = j + gap; i < size; i++) {
if (arr[i] < arr[i - gap]) { // 如果大于,不需要插入排序
int pivot = arr[i];
int t = i - gap;
while (t >= 0 && pivot < arr[t]) {
arr[t + gap] = arr[t];
t = t - gap;
}
arr[t + gap] = pivot;
}
}
}
}
}

归并排序

归并的含义是:将两个或两个以上的有序表合并成一个新的有序表。
归并排序的思路是:
假设初始表含有 n 个记录,可看成是 n 个有序的子表,每个子表的长度为1,然后两两归并,
得到 n/2 个长度为 2 的有序子表,再两两归并,如此重复,直至得到长度为 n 的有序子表为止。

合并两个有序表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 将arr[low]~arr[center]与arr[center+1]~arr[right]合并成有序表
public static void merge(int[] arr, int left, int center, int right) {
int[] result = new int[right - left + 1];
int i = left, j = center + 1, k = 0;
while (i <= center && j <= right) {
if (arr[i] < arr[j])
result[k++] = arr[i++];
else
result[k++] = arr[j++];
}
while (i <= center)
result[k++] = arr[i++];
while (j <= right)
result[k++] = arr[j++];
System.arraycopy(result, 0, arr, left, right - left + 1);
}

一趟归并:
假设长度为n的待排序序列中,每个有序表的长度为 step,归并前共有n/step个子序列:
arr[0]~arr[step-1], arr[step]~arr[step*2-1], ··· ,一趟归并将相邻的一对有序表进行归并。
需要考虑三种情况:

  • 有序表的个数为偶数,且长度均为step
  • 有序表的个数为偶数,但最后一个有序表的长度小于step
  • 有序表的个数为奇数(轮空,不需要归并)
1
2
3
4
5
6
7
8
9
10
11
12
// 子表的长度为 step,对数组进行一趟归并
public static void mergePass(int[] arr, int step) {
int length = arr.length;
int i = 0;
// 循环,归并长度为 step 的两个有序表
for (; i + step * 2 - 1 < length; i += step * 2) {
merge(arr, i, i + step - 1, i + step * 2 - 1);
}
if (i + step < length)
merge(arr, i, i + step - 1, length - 1);
// 注意: 若 i + step >= length, 最后一个子表轮空,无需归并
}

归并排序时,有序表的初始长度为1,每趟归并后有序表长度增大一倍;
若干趟归并后,有序表的长度>=n,排序结束。

1
2
3
4
5
public static void mergeSort(int[] arr, int size) {
for (int i = 1; i < size; i *= 2) {
mergePass(arr, i);
}
}

直接选择排序

算法思路:第一趟排序将待排序记录 arr[0]~arr[n-1]作为无序区,从中找出最小的记录并与无序区
第1个记录arr[0]交换,此时得到有序区为arr[0],无序区为arr[1]~arr[n-1]
第二趟排序从arr[1]~arr[n-1]选出最小的记录,与arr[1]交换。
重复上述过程…

1
2
3
4
5
6
7
8
9
10
11
public static void selectSort(int[] arr, int size) {
for (int i = 0; i < size; i++) {
int min = i;
for (int j = i + 1; j < size; j++) {
if (arr[j] < arr[min])
min = j;
}
if (min != i)
swap(arr, min, i);
}
}

堆排序

堆排序需要建立最小堆,参见这篇文章 http://www.alijava.com/heap-sort/

排序汇总

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定
冒泡 O(n2) O(n2) O(1) 稳定
快排 O(nlogn) O(n2) O(logn) 不稳定
插入 O(n2) O(n2) O(1) 稳定
希尔 O(n1.3) O(n2) O(1) 不稳定
归并 O(nlogn) O(nlogn) O(n) 稳定
选择 O(n2) O(n2) O(1) 不稳定
堆排序 O(nlogn) O(nlogn) O(1) 不稳定

利用递归实现快速排序,需要O(logn)的辅助空间;
归并排序大多数实现方法是O(logn)的辅助空间,少数是 O(1);
常见的稳定的排序为:冒泡、插入、归并。

经典排序之 堆排序(附两种建堆思路及代码)

堆排序

先做下记录,后序加上讲解
两种建立堆,一种就地,一种是插入

buildMaxHeap

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
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
int num = Integer.parseInt(scan.nextLine());
String[] inputs = scan.nextLine().split(" ");
int[] origins = new int[num];
int i = 0;
for (String s : inputs) {
origins[i++] = Integer.parseInt(s);
}
heapSort(origins, num);
for (int oi = num - 1; oi >= 0; oi--)
System.out.print(origins[oi] + " ");
System.out.println();
}
}
public static void heapSort(int[] arr, int length) {
buildHeapInPlace(arr, length);
for (int i = 0; i < length; i++) {
swap(arr, 0, length - 1 - i);
minHeap(arr, 0, length - 1 - i);
}
}
public static void buildHeapInPlace(int[] arr, int length) {
for (int i = (length - 1 - 1) >>> 1; i >= 0; i--)
minHeap(arr, i, length);
}
public static void minHeap(int[] arr, int mark, int length) {
int left = (mark << 1) + 1;
int right = (mark << 1) + 2;
while (left < length) {
int min = left;
if (right < length && arr[right] < arr[left])
min = right;
if (arr[min] < arr[mark]) {
swap(arr, min, mark);
mark = min;
left = (mark << 1) + 1;
right = (mark << 1) + 2;
} else {
break;
}
}
}
public static void swap(int[] arr, int one, int two) {
int temp = arr[one];
arr[one] = arr[two];
arr[two] = temp;
}
}

maxHeapInsert

逐个插入,建立堆

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
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
int num = Integer.parseInt(scan.nextLine());
String[] inputs = scan.nextLine().split(" ");
int[] origins = new int[num];
int i = 0;
for (String s : inputs) {
origins[i++] = Integer.parseInt(s);
}
heapSortTwo(origins, num);
for (int oi = num - 1; oi >= 0; oi--)
System.out.print(origins[oi] + " ");
System.out.println();
}
}
public static void heapSortTwo(int[] arr, int length) {
int[] copy = buildHeapInsert(arr, length);
for (int i = 0; i < length; i++) {
swap(copy, 0, length - 1 - i);
minHeapDown(copy, 0, length - 1 - i);
}
System.arraycopy(copy, 0, arr, 0, length);
}
/**
* 逐个插入建立最小堆
* @param arr
* @param length
*/
public static int[] buildHeapInsert(int[] arr, int length) {
int[] heapArr = new int[length];
int heapLength = 0;
for (int i = 0; i < length; i++) {
heapArr[i] = arr[i];
minHeapUp(heapArr, i, ++heapLength);
}
return heapArr;
}
/**
* 维持堆的性质 上溯
* @param arr
* @param mark
* @param length
*/
public static void minHeapUp(int[] arr, int mark, int length) {
int parent = (mark - 1) >> 1;
while (parent >= 0) {
if (arr[parent] > arr[mark]) {
swap(arr, parent, mark);
mark = parent;
parent = (mark - 1) >> 1;
} else {
break;
}
}
}
public static void heapSortOne(int[] arr, int length) {
buildHeapInPlace(arr, length);
for (int i = 0; i < length; i++) {
swap(arr, 0, length - 1 - i);
minHeapDown(arr, 0, length - 1 - i);
}
}
/**
* 就地改变数组,建立堆
* @param arr
* @param length
*/
public static void buildHeapInPlace(int[] arr, int length) {
for (int i = (length - 1 - 1) >>> 1; i >= 0; i--)
minHeapDown(arr, i, length);
}
/**
* 维持堆的性质 下溯
* @param arr
* @param mark
* @param length
*/
public static void minHeapDown(int[] arr, int mark, int length) {
int left = (mark << 1) + 1;
int right = (mark << 1) + 2;
while (left < length) {
int min = left;
if (right < length && arr[right] < arr[left])
min = right;
if (arr[min] < arr[mark]) {
swap(arr, min, mark);
mark = min;
left = (mark << 1) + 1;
right = (mark << 1) + 2;
} else {
break;
}
}
}
public static void swap(int[] arr, int one, int two) {
int temp = arr[one];
arr[one] = arr[two];
arr[two] = temp;
}
}

备注

可以通过这个牛客网 在线排序 OJ 检测自己排序算法

http://www.nowcoder.com/questionTerminal/508f66c6c93d4191ab25151066cb50ef

深入解析 Java 数组 Arrays.copyOf 、Arrays.asList

前言

java 数组定义和 C 语言中有些区别, Arrays 类提供了一些工具方法,本文总结了copyOf()asList()使用过程中的一些注意点。

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

数组基础

定义数组

1
2
int[] a1 = { 1, 2, 3 }; //Java 风格
int a1[] = { 1, 2, 3 }; //C语言风格

int[] a1[] 写在前面,更强调类型信息:数组int[]

int[] 也是继承了Object类, 而不是 Object[]

数组可以创建时赋值,或者创建后再赋值

1
2
3
4
5
6
7
8
int[] a2 = new int[] { 1, 2, 3 };
int a3[] = new int[] { 1, 2, 3 };
int[] a4 = new int[3];
for (int i = 0; i< a4.length; i++)
a4[i] = i;

数组长度是 length,而字符串String获取长度是length()

foreach 循环

JDK 5.0 引进了被称为foreach循环来遍历数组

1
2
for(int element: a4)
System.out.println(element);

输出

1
2
3
0
1
2

数组作为方法的参数

如果方法的参数是 int[]

  • 一是将变量传进去
  • 二是匿名格式 new int[] { 1, 2, 3 }, 传{ 1, 2, 3 }是错误的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
// testArr(a4); // 传入数组的引用,正确
testArr(new int[] { 1, 2, 3 });
// compile error
// testArr({ 1, 2, 3 });
// compile error
// testArr(1, 2, 3);
}
public static void testArr(int[] nums) {
for (int element : nums)
System.out.println(element);
}

输出

1
2
3
1
2
3

对于可变长参数的方法

1
2
3
4
public static void testVariPara(int... nums) {
for (int element : nums)
System.out.println(element);
}

下面这两种传递参数方法都可以正常运行

1
2
testVariPara(new int[] { 1, 2, 3 });
testVariPara(1, 2, 3);

Arrays 工具类

toString

Java 没有覆盖(override) 数组int[] 的toString()方法, 所以

1
System.out.println(a1.toString());

得到结果

1
[I@182da3d

可以使用工具类 Arrays 输出数组

1
System.out.println(Arrays.toString(a1));

输出

1
[1, 2, 3]

copyOf

1
2
3
4
5
6
7
int[] arr = {1,2,3,4,5};
int[] copied = Arrays.copyOf(arr, 10); //10 是数组的长度
System.out.println(Arrays.toString(copied));
copied = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(copied));

输出

1
2
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[1, 2, 3]

区别 System.arraycopy()

1
2
3
4
5
6
int[] arr = {1,2,3,4,5};
int[] copied = new int[10];
System.arraycopy(arr, 0, copied, 1, 5);// 5是需复制的元素个数
System.out.println(Arrays.toString(copied));

输出

1
2
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 2, 3, 4, 5, 0, 0, 0, 0]

查看 Arrays.copyOf 源码可知,它是新建了一个数组,然后利用System.arraycopy 将源数组复制到新数组中

1
2
3
4
5
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}

asList 使用

1
2
int[] a1 = { 1, 2, 3 };
List<int[]> list = Arrays.asList(a1); // eclipse 提示修改为这样可以编译通过

上面的很奇怪, List<int>List<Integer> 会编译不通过,只能改为 List<int[]>

转为 List<int[]> 可以通过这样访问数组 list.get(0)

为什么会这样呢?

List<int>编译错误,说明没有自动装箱和拆箱,Arrays.asList常见用法如下

1
List<Object> objList = Arrays.asList(obj1, obj2, obj3);

上面代码将多个输入参数转化为 List,你也可以传入单个参数

1
List<Object> objList = Arrays.asList(obj1);

而前面也提到了 int[]是一个对象,继承自Object , 所以返回了类型参数为 int[]List

推荐 Apache Commons ArrayUtils
下载地址 https://commons.apache.org/proper/commons-lang/download_lang.cgi

它提供了很多工具方法,可以使用下面的代码处理 int 数组

1
List<Integer> exampleList = Arrays.asList(ArrayUtils.toObject(array));

看下 API,public static Integer[] toObject(int[] array) 是将 int[] 转为 Integer[]

也可以这么使用 Arrays.asList 来转化数组

1
2
3
Integer[] a1 = { 1, 2, 3 };
List<Integer> list = Arrays.asList(a1);
System.out.println(list);

或者这样

1
2
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list);

asList 定长

1
2
List<Integer> list = Arrays.asList(1, 2, 3);
list.add(44); // 抛出 UnsupportedOperationException

上面的代码会抛出java.lang.UnsupportedOperationException异常

注意:Arrays.asList返回的是 Arrays.ArrayList,不是我们常用的ArrayList

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
// Arrays.ArrayList 源码
private static class ArrayList<E> extends AbstractList<E> {
public E get(int index) {
return a[index];
}
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i<a.length; i++)
if (a[i]==null)
return i;
} else {
for (int i=0; i<a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}

Arrays.asList 不支持改变数组长度的操作(addremove等),看下源码,Arrays.ArrayList只实现了 getsetindexOfcontains等方法。

Arrays.ArrayList的父类是AbstractList,它的 addset都是throw new UnsupportedOperationException()

1
2
3
4
5
6
7
8
9
10
// AbstractList 源码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
}

如果需要得到支持addremoveArrayList,可以这样

1
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3));

集合转为数组

ArrayList 可以转化为数组,有两个重载的方法

  • Object[] toArray()
  • <T> T[] toArray(T[] a)

注意 toArray() 只能得到 Object[],无法转型为具体数组类型,比如下面的代码会抛出java.lang.ClassCastException异常

1
2
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Integer[] arr = (Integer[]) list.toArray(); // 抛出ClassCastException

如果需要得到具体类型,需要使用toArray(T[] a),传入参数T[] a

  • T[] a长度< 原来list的长度,则新建一个list的长度数组,填充元素并返回

  • 若长度 >= list的长度,则把list元素填充T[] a,并返回T[] a的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Integer[] a1 = new Integer[2];
list.toArray(a1);
System.out.println(Arrays.toString(a1));
Integer[] a2 = new Integer[3];
list.toArray(a2);
System.out.println(Arrays.toString(a2));
Integer[] a3 = new Integer[4];
list.toArray(a3);
System.out.println(Arrays.toString(a3));

输出

1
2
3
[null, null]
[1, 2, 3]
[1, 2, 3, null]

也可以这么使用String[] a = list.toArray(new String[0]);new String[0]只是为了类型信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// toArray(T[] a) 源码
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}

参考文章

Android Material Design 之路之 RecyclerView 学习笔记

前言

Android Material Design越来越流行,以前很常用的 ListView 现在也用RecyclerView代替了,实现原理还是相似的。笔者实现一下 RecyclerView,代码比较简单,适合初学者,如有错误,欢迎指出。

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

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

复习 ListView

ListView 实现过程中需要复写BaseAdapter,主要是这4个方法

  • public int getCount() :适配器中数据集中 数据的个数,即ListView需要显示的数据个数
  • public Object getItem(int position) : 获取数据集中与指定索引对应的数据项
  • public long getItemId(int position) : 获取指定行对应的ID
  • public View getView(int position, View convertView, ViewGroup parent) :获取每一个Item的显示内容

一般 ListView 每一项都是相同的布局,若想各个项实现不同的布局,可复写 getItemViewTypegetViewTypeCount实现

RecyclerView 实现

xml 布局

RecyclerView中每一项的布局 item_article_type_1.xml

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
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/cv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="5dp"
app:contentPadding="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/rcv_article_photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
fresco:actualImageScaleType="centerInside"
fresco:roundAsCircle="true"
fresco:roundingBorderColor="@color/lightslategray"
fresco:roundingBorderWidth="1dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/rcv_article_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="关于举办《经典音乐作品欣赏与人文审美》讲座的通知"
android:textColor="@color/primary_text" />
<!-- 新闻 发布时间 来源 阅读次数-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/rcv_article_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="2dp"
android:text="2015-01-09" />
<TextView
android:id="@+id/rcv_article_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:text="科学研究院" />
<TextView
android:id="@+id/rcv_article_readtimes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:text="1129次" />
</LinearLayout>
<TextView
android:id="@+id/rcv_article_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:maxLines="2"
android:text="讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识..." />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

布局思路就是 CardView里面嵌入了一个LinearLayout。图片部分用固定宽度100dp,文字部分利用android:layout_weight="1"占据了其他部分。

TextView利用android:gravity="center"使得标题的文字居中。

LinearLayout里面利用android:gravity="center"使得"2015-01-09 科学研究院 1129次"居中,

新闻详情内容的TextView利用

1
2
android:maxLines="2"
android:ellipsize="end"

将文章内容限定为2行,超出部分用省略号显示。

预览效果

这里写图片描述

新闻列表的 xml 文件,fragment_article.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_article"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

实现 Adapter

主要步骤是:

  • 根据上面的 item_article_type_1.xml实现一个 class ImageItemArticleViewHolder extends RecyclerView.ViewHolder
  • 实现ItemArticleListAdapter extends RecyclerView.Adapter <>
    重写三个方法
    • public int getItemCount()
    • public TestAdapter.ImageItemArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    • public void onBindViewHolder(ImageItemArticleViewHolder holder, int position)
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
public class ItemArticleAdapter extends RecyclerView.Adapter<ItemArticleAdapter.ImageItemArticleViewHolder> {
//新闻列表
private List<ItemArticle> articleList;
//context
private Context context;
private LayoutInflater mLayoutInflater;
public ItemArticleAdapter(Context context,List<ItemArticle> articleList) {
this.context = context;
this.articleList = articleList;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public ItemArticleAdapter.ImageItemArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(
R.layout.item_article_type_1, parent, false);
return new ImageItemArticleViewHolder(view);
}
@Override
public void onBindViewHolder(ImageItemArticleViewHolder holder, int position) {
ItemArticle article = articleList.get(position);
holder.rcvArticlePhoto.setImageURI(Uri.parse(article.getImageUrl()));
holder.rcvArticleTitle.setText(article.getTitle());
holder.rcvArticleDate.setText(article.getPublishDate());
holder.rcvArticleSource.setText(article.getSource());
//注意这个阅读次数是 int 类型,需要转化为 String 类型
holder.rcvArticleReadtimes.setText(article.getReadTimes()+"次");
holder.rcvArticlePreview.setText(article.getPreview());
}
@Override
public int getItemCount() {
return articleList.size();
}
class ImageItemArticleViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.rcv_article_photo)
SimpleDraweeView rcvArticlePhoto;
@InjectView(R.id.rcv_article_title)
TextView rcvArticleTitle;
@InjectView(R.id.rcv_article_date)
TextView rcvArticleDate;
@InjectView(R.id.rcv_article_source)
TextView rcvArticleSource;
@InjectView(R.id.rcv_article_readtimes)
TextView rcvArticleReadtimes;
@InjectView(R.id.rcv_article_preview)
TextView rcvArticlePreview;
public ImageItemArticleViewHolder(View itemView) {
super(itemView);
ButterKnife.inject(this, itemView);
}
}
}

新闻实体类 javabean

有新闻的 index,图片 url,标题,发布时间,来源,阅读次数,新闻内容预览

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
/**
* 新闻类,这是在 RecycleView 使用的新闻 javabean
* 还有一个新闻详情javabean
*/
public class ItemArticle {
private int index;
private String imageUrl;
private String title;
private String publishDate;
private String source;
private int readTimes;
private String preview;
public ItemArticle(int index, String imageUrl, String title, String publishDate, String source, int readTimes, String preview) {
this.index = index;
this.imageUrl = imageUrl;
this.title = title;
this.publishDate = publishDate;
this.source = source;
this.readTimes = readTimes;
this.preview = preview;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPublishDate() {
return publishDate;
}
public void setPublishDate(String publishDate) {
this.publishDate = publishDate;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public int getReadTimes() {
return readTimes;
}
public void setReadTimes(int readTimes) {
this.readTimes = readTimes;
}
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
}

fragment 里面使用 RecyclerView

思路就是开启一个异步线程,读取多条新闻,加入List<ItemArticle>,由这个List构造ItemArticleAdapter,最后利用setAdapter()方法给RecyclerView加上适配器。

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
public class ArticleFragment extends Fragment {
private static final String STORE_PARAM = "param";
@InjectView(R.id.rcv_article)
RecyclerView rcvArticle;
private String mParam;
//新闻列表数据
private List<ItemArticle> itemArticleList = new ArrayList<ItemArticle>();
//获取 fragment 依赖的 Activity,方便使用 Context
private Activity mAct;
public static Fragment newInstance(String param) {
ArticleFragment fragment = new ArticleFragment();
Bundle args = new Bundle();
args.putString(STORE_PARAM, param);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam = getArguments().getString(STORE_PARAM);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_article, null);
Log.i(STORE_PARAM, "in StoreFragment");
mAct = getActivity();
ButterKnife.inject(this, view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
rcvArticle.setLayoutManager(new LinearLayoutManager(mAct));//这里用线性显示 类似于listview
// rcvArticle.setLayoutManager(new GridLayoutManager(mAct, 2));//这里用线性宫格显示 类似于grid view
// rcvArticle.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));//这里用线性宫格显示 类似于瀑布流
new LatestArticleTask().execute();
}
@Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.reset(this);
}
class LatestArticleTask extends AsyncTask<String, Void, List<ItemArticle>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected List<ItemArticle> doInBackground(String... params) {
ItemArticle storeInfo1 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo2 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo3 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo4 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo5 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
ItemArticle storeInfo6 =
new ItemArticle(20123, "http://i2.sinaimg.cn/ent/j/2012-05-20/U5912P28T3D3634984F328DT20120520152700.JPG", "关于举办《经典音乐作品欣赏与人文审美》讲座的通知", "2015-01-09", "科学研究院", 1129,
"讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识...");
itemArticleList.add(storeInfo1);
itemArticleList.add(storeInfo2);
itemArticleList.add(storeInfo3);
itemArticleList.add(storeInfo4);
itemArticleList.add(storeInfo5);
itemArticleList.add(storeInfo6);
return itemArticleList;
}
@Override
protected void onPostExecute(List<ItemArticle> data) {
super.onPostExecute(data);
ItemArticleAdapter adapter = new ItemArticleAdapter(mAct, data);
rcvArticle.setAdapter(adapter);
}
}
}

效果图

利用修改布局,线性显示或者宫格显示 (以前宫格显示很麻烦,现在一条命令就好了)

1
2
3
rcvArticle.setLayoutManager(new LinearLayoutManager(mAct));//这里用线性显示 类似于listview
// rcvArticle.setLayoutManager(new GridLayoutManager(mAct, 2));//这里用线性宫格显示 类似于grid view
// rcvArticle.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));//这里用线性宫格显示 类似于瀑布流

效果图

线性布局

统计结果1

线性宫格布局

统计结果1

总结

int 转化为 String

TextViewsetText(int resid) 方法,但是这儿 int 表示 resourceId,如果我想把阅读次数(int 1123)赋给这个 TextView,不能使用这个方法。

1
2
3
4
5
6
//int 转 String 有三种方法
int i =8;
String s =Integer.toString(i);
String g =String.valueOf(i);
String h =i+"";
holder.rcvArticleReadtimes.setText(String.valueOf(article.getReadTimes()));

Todo List

  • Picasso 图片缓存库的学习
  • 实现 RecyclerView 每个项各自的布局

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

参考文章

Android RecyclerView Example | Java Techig

Android UI布局之区分 android:gravity 和 android:layout_gravity

前言

在进行UI布局的时候,可能经常会用到 android:gravityandroid:layout_Gravity 这两个属性。

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

一言以蔽之

  • android:gravity 组件的子组件在组件中的位置

  • android:layout_gravity 组件自身在父组件中的位置

《第一行代码里》里的解释

  • android:gravity用于指定文字在控件中的对齐方式
  • android:layout_gravity用于指定控件在布局中的对齐方式

其实个人觉得两种说法并不矛盾,文字也可以理解为TextView的子组件,下面我们会看到android:gravity分别用于设置子组件或者文字。

网上还有一种总结是:

  • 名称不以layout_开头的属性作用于组件本身,组件生成时,会调用某个方法按照属性及属性值进行自我配置。

  • 名称以layout_开头的属性作用于组件的父组件,称这些属性为布局参数,它们会告知父布局如何在内部安排自己的子元素。

android:gravity

android:gravity用于指定文字在控件中的对齐方式(这儿将文字看作是子控件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width = "match_parent"
android:layout_height="wrap_content"
<!-- gravity 作用于文字 -->
android:gravity="center"
android:textSize="24sp"
android:padding = "24dp"
android:text="this is text" />
</LinearLayout>

效果见下图:

统计结果1


当然 android:gravity="center"也可以指定控件 TextViewLinearLayout中居中,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!-- gravity 作用于子控件 TextView -->
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width = "match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:padding = "24dp"
android:text="this is text" />
</LinearLayout>

再看效果图:

统计结果1

TextView就居于LinearLayout的正中了。

  • LinearLayoutandroid:gravity="center"表示TextView居于LinearLayout的正中间
  • TextViewandroid:gravity="center"表示文字处于TextView的中间

android:layout_gravity

android:layout_gravity用于指定控件在布局中的对齐方式。注意,当LinearLayoutvertical时,只有水平方向上的对齐才会生效。

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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width = "match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:padding = "24dp"
android:text="this is text" />
<TextView
android:layout_width = "wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:padding = "24dp"
<!-- layout_gravity 作用于水平方向上 end -->
android:layout_gravity="end"
android:text="another text" />
</LinearLayout>

效果图:
统计结果1

第2个TextView中,android:gravity表示这个TextView处于LinearLayout的中间,而android:layout_gravity="end"表示处于水平end的位置。

最佳实践 完整示例

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
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/cv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="5dp"
app:contentPadding="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/rcv_article_photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
fresco:actualImageScaleType="centerInside"
fresco:roundAsCircle="true"
fresco:roundingBorderColor="@color/lightslategray"
fresco:roundingBorderWidth="1dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/rcv_article_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
<!-- gravity 文字居中 -->
android:gravity="center"
android:text="关于举办《经典音乐作品欣赏与人文审美》讲座的通知"
android:textColor="@color/primary_text" />
<!-- 新闻 发布时间 来源 阅读次数-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
<!-- 3个TextView居中 -->
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/rcv_article_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="2dp"
android:text="2015-01-09" />
<TextView
android:id="@+id/rcv_article_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:text="科学研究院" />
<TextView
android:id="@+id/rcv_article_readtimes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:text="1129次" />
</LinearLayout>
<TextView
android:id="@+id/rcv_article_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:maxLines="2"
android:text="讲座主要内容:以中、西方音乐历史中经典音乐作品为基础,通过作曲家及作品创作背景、相关音乐文化史知识及音乐欣赏常识..." />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

效果图
这里写图片描述

思路

这是自己项目中的一个记录,具体可参照这篇文章
Android Material Design学习之RecyclerView代替 ListView http://www.alijava.com/recyclerview-basics/


布局思路就是 CardView里面嵌入了一个LinearLayout

图片部分用固定宽度100dp,文字部分利用android:layout_weight="1"占据了其他部分。

TextView利用android:gravity=”center”使得标题的文字居中。

LinearLayout里面利用android:gravity="center"使得"2015-01-09 科学研究院 1129次"居中,

新闻详情内容的TextView利用

1
2
android:maxLines="2"
android:ellipsize="end"

将文章内容限定为2行,超出部分用省略号显示。