effective java学习笔记

1.考虑用静态工厂方法代替构造器

  • 静态方法有名称,通过静态方法可以很明确的知道返回的是什么对象。例如BigInteger.probablePrime()
  • 静态方法不必每次都创建一个新的实例。对于某些返回对象是不可变类时可以提升性能。例如Boolean.valueOf(boolean)
  • 静态方法可以返回任何类型的子类型。这样面对接口编程的时候,我们可以不关心返回对象的具体的实现,只关心接口暴露的方法即可
  • 在创建参数化实例的时候,可以使代码变得更加简洁。比如:
1
2
3
4
5
Map<String, List<String>> m = new HashMap<String, List<String>>();
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K ,V>();
}
Map<String, List<String>> m = HashMap.newInstance();

2.在遇到多个构造器参数时考虑用Builder

1
2
3
4
5
6
7
8
9
10
11
public class Person {
private int mAge;
private String mName;
private int mHeight;
public Person(int age, String name, int height) {
mAge = age;
mName = name;
mHeight = height;
}
}

例如一个对象有多个属性需要设置,我们可以在构造方法中设置,如下,但是在调用时会
Person person = new Person(19, “名字”, 180);如果参数一旦变多则维护起来略麻烦,这时会考虑使用set方法,但是使用set方法在多线程中是不安全的,这时可以考虑采用Builder模式。

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
public class Person {
private int mAge;
private String mName;
private int mSex;
private Person(Builder builder) {
mAge = builder.mAge;
mName = builder.mName;
mSex = builder.mSex;
}
public static class Builder {
private int mAge;
private String mName;
private int mSex;
public Builder name(String name) {
mName = name;
return this;
}
public Builder age(int age) {
mAge = age;
return this;
}
public Builder sex(int sex) {
mSex = sex;
return this;
}
public Person build() {
return new Person(this);
}
}
}
//调用时
Person person = new Person.Builder()
.age(19)
.name("名字")
.sex(1)
.build();

通过上面这种方式很清晰。

3.用私有构造器或者枚举属性强化Singleton属性

在java1.5后可以通过枚举来实现单例,这种方法简洁,而且创建的过程是线程安全的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum Person {
INSTANCE;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
}
Person.INSTANCE.setName("kevin");
System.out.println(Person.INSTANCE.getName());

4.通过私有构造器强化不可实例化的能力

对于一些工具类来说,它只存在静态方法,这时它并不想被实例化,因此最好强化不可实例化的能力,给它添加私有构造器。

5.避免创建不必要的对象

String s = new String("kevin");

在这里”kevin” 本来就是一个String对象,这时就多创建了一个对象。
同时除了重用不可变对象之外,也可以重用已知不会被修改的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public enum Person {
private final Date birthDate;
public boolean isBabyBoomer() {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965 ,Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0
&& birthDate.compareTo(boomEnd) < 0;
}
}
//在这里start和end都是不变的,而每次比较时都会生成2个Date同样的对象。
public enum Person {
private final Date birthDate;
private static Date boomStart;
private static Date boomEnd;
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
boomStart = gmtCal.getTime();
gmtCal.set(1965 ,Calendar.JANUARY, 1, 0, 0, 0);
boomEnd = gmtCal.getTime();
}
public boolean isBabyBoomer() {
return birthDate.compareTo(boomStart) >= 0
&& birthDate.compareTo(boomEnd) < 0;
}
}

因为Java的自动装箱机制导致下面的代码会产生很多对象

1
2
3
4
Long l = 0L;
for(long i = 0;i < Long.MAX_VALUE; i++) {
l += i;
}

6.消除过期的引用

Java

git笔记

撤销操作

git commit –amend 用当前暂存区的快照提交,同时可以对上次的commit信息进行修改

git commit -m "first commit"
git add forget_file.txt
git commit --amend "second commit"
//以上将漏掉的文件添加进暂存区,但是commit log上只存在second commit

git reset HEAD file 将当前文件从暂存区回到工作区
git checkout – file 将在工作区进行修改的文件还原

版本回退

git reset [HEAD^][commit id] 回退到上一个版本或commit id指定的版本,将之前的修改放入工作区。 git reset –hard [HEAD^][commit id] 回退,但是工作区不包含修改
git reflog可以查看历史commit id,用于重置版本回退

git stash

git stash 可以将目前分支的工作区的修改暂存到一个栈中,可以多次通过git stash压栈,同时可以通过git stash list查看栈,git stash pop出栈并将修改放入当前分支的工作区,git stash apply stash@{id}不出栈然后将修改放入当前分支的工作区,git stash clear清栈

Retrofit分享

Retrofit简介

Retrofit是Square开发的一个可以用于Android和Java的网络请求封装库。

优点

  1. Retrofit可以利用接口,方法和注解来创建一个请求,方便管理。
  2. Retrofit速度较快image
  3. 可将response自动包装成Java对象

依赖配置

在1.x默认集成Gson库,不会集成OkHttp,如果需要OkHttp作为HTTP client,则需手动添加依赖

1
2
3
4
5
dependencies {
// Retrofit & OkHttp
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.7.2'
}

在2.0时默认集成OkHttp,不会集成Gson

1
2
3
4
5
dependencies {
// Retrofit & Gson
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
}

android studio快捷键

  • 添加书签:F3
    • 显示书签列表:cmd + F3
  • 隐藏其他窗口:control + w(自定义)
  • 定位到父类的方法:cmd + u
  • 在finder中打开文件:cmd + 点击tab
  • 查看方法参数列表:cmd + p
  • 快速查看方法定义:cmd + y
  • Esc:该操作仅仅把光标移回编辑器。
    Shift + Esc:该操作会关闭当前面板,然后把光标移回到编辑器。
  • 按住Alt竖排选中
  • 语句补全:cmd + shift + enter
  • 代码补全时enter不对后面修改tab对后面修改
  • cmd + alt + m 提取方法
  • contral + alt + h查看调用层级

Device Compatibility

Device feature

Android系统对每个硬件或者软件 feature定义了相应feature id,如果你不希望没有某个feature的设备下载你的app时,你可以在manifest里写:

1
2
3
4
5
<manifest ... >
<uses-feature android:name="android.hardware.sensor.compass"
android:required="true" />
...
</manifest>

也可以设置成false,表明不需要,可以在运行时动态检查:

1
2
3
4
5
6
PackageManager pm = getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
// This device does not have a compass, turn off the compass feature
disableCompassFeature();//自定义方法处理相应逻辑
}

Platform version

Android是向前兼容的,4.0是兼容2.3的但是却无法跑4.0之后的,因此如果自己的app用到了比较新的api,这时是无法在旧版本上跑的,因此你需要声明你app所支持的最低sdk版本,target版本则是你app充分利用的最高版本,例如当你的app的tagrgetVersion是19,但是用到的某个api在21时更新了,这时你的app会跑19,而不会跑21,这时就需要注意targetSdkVersion这个属性了

1
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21"/>
1
2
3
4
5
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Running on something older than API level 11, so disable
// the drag/drop features that use ClipboardManager APIs
disableDragAndDrop();
}

Android Training重点

适配

Android屏幕有两种属性,size 和 density

size: small,normal,large,xlarge
density: low (ldpi), medium (mdpi), high (hdpi), extra high (xhdpi)

屏幕的布局(landscape or portrait)也被认为是不同的

Android系统会根据当前设备的信息来获取相应的资源。 例如将某个布局资源放在res/layout-large下时,当屏幕为large时,会取这里的。

提供资源列表

文字:txtName.setText(getResources().getText(R.string.name));

图片:imgIcon.setBackgroundDrawableResource(R.drawable.icon);

颜色:txtName.setTextColor(getResouces().getColor(R.color.red));

布局:setContentView(R.layout.main);

控件:txtName = (TextView)findViewById(R.id.txt_name);

View相关

View基础

ActionBar

ActionBar的overflow 并不是在每个设备上都显示的,如果有menu物理键时则不会显示,可以通过如下设置:

1
2
3
4
5
6
7
8
9
10
private void setOverflowShowingAlways() {
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
} catch (Exception e) {
e.printStackTrace();
}
}

LayoutInflater

inflate(int resource, ViewGroup root, boolean attachToRoot)

  1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
  2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
  3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性以root为父容器进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。

LinearLayout

  1. android:layout_weight的真实含义是:一旦View设置了该属性(假设有效的情况下),那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比!对于所有的View默认的权重是0,如果只设置了一个View的权重大于0,则该View将占据除去别的View本身占据的空间的所有剩余空间。因此这里设置EditText的权重为1,使其能够占据除了按钮之外的所有空间。
  1. 当 android:orientation=”vertical” 时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。
    即:layout_gravity = left,right,center_horizontal 是生效的。
    当 android:orientation=”horizontal” 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。
    即:top,bottom,center_vertical 是生效的。
    3.margin与padding的区别:首先margin代表的是偏移,比如marginleft = “5dp”表示组件离容器左边缘偏移5dp; 而padding代表的则是填充,而填充的对象针对的是组件中的元素,比如TextView中的文字
    比如为TextView设置paddingleft = “5dp”,则是在组件里的元素的左边填充5dp的空间!
    margin针对的是容器中的组件,而padding针对的是组件中的元素,要区分开来!

TextView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends Activity {
private TextView txtZQD;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtZQD = (TextView) findViewById(R.id.txtZQD);
Drawable[] drawable = txtZQD.getCompoundDrawables();
// 数组下表0~3,依次是:左上右下
drawable[1].setBounds(100, 0, 200, 200);
txtZQD.setCompoundDrawables(drawable[0], drawable[1], drawable[2],
drawable[3]);
}
}

①Drawable[] drawable = txtZQD.getCompoundDrawables( );
获得四个不同方向上的图片资源,数组元素依次是:左上右下的图片
②drawable1.setBounds(100, 0, 200, 200);
接着获得资源后,可以调用setBounds设置左上右下坐标点,比如这里设置了代表的是:
长是:从离文字最左边开始100dp处到200dp处
宽是:从文字上方0dp处往上延伸200dp!
③txtZQD.setCompoundDrawables(drawable[0], drawable1, drawable2,
drawable3);为TextView重新设置drawable数组!没有图片可以用null代替哦!
PS:另外,从上面看出我们也可以直接在Java代码中调用setCompoundDrawables为
TextView设置图片!

  • TextView在XML中可以设置autoLink属性,值如果是all,则根据协议头匹配。 在java代码中:
1
2
3
TextView txt = (TextView) findViewById(R.id.txt_link);
txt.setAutoLinkMask(Linkify.ALL);
txt.setMovementMethod(LinkMovementMethod.getInstance());//必须存在,此方法在需要响应用户事件时使用,如点击一个电话号码就跳转到拨号页面。如果不执行这个方法是不会响应事件的,即便文本看着已经是下划线蓝色字了
  • TextView同时还支持Html标签,通过setText(Html.fromHtml(....)) 设置。常用有:

    <font>:设置颜色和字体。
    <big>:设置字体大号
    <small>:设置字体小号
    <i><b>:斜体粗体
    <a>:连接网址
    <img>:图片
    
1
2
3
4
5
TextView t1 = (TextView)findViewById(R.id.txtOne);
String s1 = "<font color='blue'><b>百度一下,你就知道~:</b></font><br>";
s1 += "<a href = 'http://www.baidu.com'>百度</a>";
t1.setText(Html.fromHtml(s1));
t1.setMovementMethod(LinkMovementMethod.getInstance());//如果想链接有效时设置
  • 除了上面的HTML可以定制我们TextView的样式外,还可以使用SpannableString和SpannableStringBuilder来完成,两者区别:前者针对的是不可变文本,而后者则是针对可变文本
  • SpannableString可供我们使用的API有下面这些:

    • BackgroundColorSpan 背景色

    • ClickableSpan 文本可点击,有点击事件

    • ForegroundColorSpan 文本颜色(前景色)

    • MaskFilterSpan 修饰效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter)

    • MetricAffectingSpan 父类,一般不用

    • RasterizerSpan 光栅效果

    • StrikethroughSpan 删除线(中划线)

    • SuggestionSpan 相当于占位符

    • UnderlineSpan 下划线

    • AbsoluteSizeSpan 绝对大小(文本字体)

    • DynamicDrawableSpan 设置图片,基于文本基线或底部对齐。

    • ImageSpan 图片

    • RelativeSizeSpan 相对大小(文本字体)

    • ReplacementSpan 父类,一般不用

    • ScaleXSpan 基于x轴缩放

    • StyleSpan 字体样式:粗体、斜体等

    • SubscriptSpan 下标(数学公式会用到)

    • SuperscriptSpan 上标(数学公式会用到)

    • TextAppearanceSpan 文本外貌(包括字体、大小、样式和颜色)

    • TypefaceSpan 文本字体

    • URLSpan 文本超链接

  • 实现滚动效果

android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
  • 字间距:

    • android:textScaleX:调节字间距的,默认值1.0f,值是float
    • Java中setScaleX(2.0f);
  • 行间距:
    Android系统中TextView默认显示中文时会比较紧凑,为了让每行保持的行间距

    • android:lineSpacingExtra:设置行间距,如”3dp”
    • android:lineSpacingMultiplier:设置行间距的倍数,如”1.2”
    • Java代码中可以通过: setLineSpacing方法来设置

EditText

  • android:selectAllOnFocus=”true” 当EditText获得焦点后会全选文本
  • inputType 限制输入类型 如 phone
  • EditText默认是多行显示的,并且能够自动换行,即当一行显示不完的时候,他会自动换到第二行
  • 另外EditText还为我们提供了设置英文字母大写类型的属性:android:capitalize
    默认none,提供了三个可选值:
    • sentences:仅第一个字母大写
    • words:每一个单词首字母大小,用空格区分单词
    • characters:每一个英文字母都大写
  • android:windowSoftInputMode
    Activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性。

Button

  • clickable在xml设置成false时,扔可点击触发事件,在java中则不会触发,enable则完全不可点击,并且有置灰效果
  • shape 可以理解成 drawable资源,可以在selector中使用

ImageView

  • ①background通常指的都是背景,而src指的是内容!!
  • ②当使用src填入图片时,是按照图片大小直接填充,并不会进行拉伸
    而使用background填入图片,则是会根据ImageView给定的宽度来进行拉伸
  • Java代码中设置blackground和src属性:
    • 前景(对应src属性):setImageDrawable( );
    • 背景(对应background属性):setBackgroundDrawable( );

ViewPager

  • mViewPager.setOffscreenPageLimit(1); 可以保存一页的状态,在第一次时,会调用两个页面的响应的oncreate(),onResume(). 默认是1.
  • 和ViewFlipper的区别:ViewFlipper继承ViewAnimator,切换view的时候是有动画效果的,适合做ppt,多界面的程序欢迎引导界面,算是个轻量级的组件,适合展示静态数据,少量数据。
    ViewPager继承ViewGroup。看官网描述,这货和Fragment是好搭档,Fragment有自己的生命周期。也就是说ViewPager更适用复杂的视图切换,而且Viewpager有自己的adapter,这也让其适应复杂对象,实现数据的动态加载。

OnNewIntent

  • 在当前Acticity A跳转到当前Activity A时,如果A是singleTop时,则会回调A的OnNewIntent,其他页跳转到改页面时不会回调
  • Activity B为singleTask时,从其他Activity跳转到B时,如果在栈中存在则它之上的Activity出栈,并回调OnNewIntent(),不存在则创建并压栈,不调用OnNewIntent.

Android自定义View

因为Android的UI 大部分都是直接通过xml文件的,但是在xml文件里我们只是写明了 一些键值对,例如 layout_height = “100dp” 一个view的显示绘制逻辑则是在类中实现。

自定义View分为几个步骤:

第一步:在valuse/arrts创建我们view的属性

1
2
3
4
5
6
<resources>
<declare-styleable name="customview">
<attr name="text" format="string" />
<attr name="testArray" format="integer" />
</declare-styleable>
</resources>

也可直接,不过在获取时略微麻烦

1
2
<attr name="text" format="string" />
<attr name="testArray" format="integer" />

第二步:在我们的自定义的view的构造方法中获取这些属性对应的值,这些值就是被我们定义在布局文件里的:例如:

1
2
3
4
5
<com.example.kevin.myapplication.MyView
android:layout_width="150dp"
android:layout_height="120dp"
spa:testArray="520"
spa:text="hello world" />

内部是通过解析xml将其封装在AttributeSet对象里,并传入view的构造参数里。因此我们在view的构造函数里是可以直接获取到定义的各个属性的值的。但是一般不推荐,因为当一些值是引用时,获取的是id,我们还需要进行转化。所以大部分是直接

TypedArray ta =context.obtainStyledAttributes(attrs,R.styleable.customview);

此时declare-styleable:

1
2
3
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.customview);
String text = ta.getString(R.styleable.customview_text);
int testArray = ta.getInteger(R.styleable.customview_testArray,1);

没有的则需要手动得到id,存入数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyTextView extends View {
private static final String TAG = MyTextView.class.getSimpleName();
private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
private static final int ATTR_ANDROID_TEXT = 0;
private static final int ATTR_TESTATTR = 1;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// ==>use typedarray
TypedArray ta = context.obtainStyledAttributes(attrs, mAttr);
String text = ta.getString(ATTR_ANDROID_TEXT);
int textAttr = ta.getInteger(ATTR_TESTATTR, -1);
//输出 text = Hello world! , textAttr = 520
Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
ta.recycle();
}
}

而写成declare-styleable的会自动生成如下:

1
2
3
4
5
6
7
8
9
10
public static final class attr {
public static final int testAttr=0x7f0100a9;
}
public static final class styleable {
public static final int test_android_text = 0;
public static final int test_testAttr = 1;
public static final int[] test = {
0x0101014f, 0x7f0100a9
};
}

第三步:如果我们的view需要支持wrap_content则需要重写onMeasure()来设置一个最大值。

第四步:根据得到的每个属性的值,用andorid原生的api来写绘制逻辑,重写onDraw()。

Activity详解

启动Activity

  • 显式Intent
1
2
Intent intent = new Intent(MainActivity.class,SecondActivity.class);
startActivity(intent);
  • 隐式Intent:并不指定某一个特定的Activity而是通过action和category还有data来指定,通过系统分析启动哪一个Activity.如果不存在相应的Activity则报错,存在多个则可以选择,其中多个action和多个data满足一个就行,category必须全部满足,没有android.intent.category.DEFAULT的Activity无法响应隐式Intent.
1
2
3
4
5
<intent-filter>
<action android:name="com.meituan.kevin.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/> //必须加,startActivity()会默认添加这个category
<category android:name="com.meituan.kevin.second.CATEGORY"/>
</intent-filter>
1
2
3
Intent intent = new Intent("com.meituan.kevin.ACTION_START");
intent.addCategory("com.meituan.kevin.second.CATEGORY");
startActivity(intent);

关闭Activity

Activity可以显示调用finish()进行关闭,但是一般不这样做,一般是由Android系统来管理,只有确实不希望用户返回这个实例时才显示调用。