Fighting!

潜行者的沉默

基础数据类型

基本数据类型分为原始类型和包装类型,Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

8种原始类型 容量 对应包装类型
byte(字节) 8 位 Byte
shot(短整型) 16位 Short
int(整型) 32 位 Integer
long(长整型) 64 位 Long
float(浮点型) 32 位 Float
double(双精度) 64 位 Double
char(字符型) 16 位 Character
boolean(布尔型) 1 位 Boolean

注意几点:

1、各数据类型按容量大小(表数范围大小)由小到大排列为:

byte <—— short, char <——int <——long <——float <——double

2、基本类型之间的转换原则:

1)运算时,容量小的类型自动转换为容量大的类型;

2)容量大的类型转换为容量小的类型时,要加强制转换符,且精度可能丢失;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如:float f = 1.2f;
int ff = (int) f;
System.out.println(ff); 1
```

3)short,char之间不会互相转换(需要强制转换),byte、short、char并且三者在计算时首先转换为int类型;

4)实数常量默认为double类型, 整数常量默认为int类型;

**3、包装类及String类都是定定义为public final class的,因此这几个都不能被继承;  **

**4、原始类型是可以通过==直接判断是否相等的,而包装类型是类,通过==判断值是否相等是不对的,必须通过equals()函数**



**下面的程序中,temp的最终值是什么? **

long temp=(int)3.9; 3

temp%=2; 1

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

A .0

B .1

C .2

D .3

E .4


**short s1 = 1; s1 = s1 + 1;有什么错(错误)? short s1 = 1; s1 += 1;有什么错(正确)?**


**下面哪个不是JAVA关键字 **

A integer

B double

C float

D default


###2.1.2 历年考题

**下面哪些不是java的简单数据类型?** AC

A. short B. Boolean C. Double D. float


**0.6332的数据类型是()** A

A float B double C Float D Double


**float f=3.4;是否正确?** 错



**下面哪些类可以被继承**

下面哪些类可以被继承? ABDE
Java.lang.Thread、java.lang.Number、java.lang.Double、java.lang.Math、 java.lang.ClassLoader
A、Thread B、Number C、Double D、Math E、ClassLoader


**String 是最基本的数据类型吗?**不是


**int和Integer有什么区别?** 原始类型和封装类型,java5后自动拆箱和封箱机制


**5、请写出下面程序的运行结果:**
 

class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
System.out.println(b == c); // true b自动装箱然后自动拆箱成int类型再和c比较

}

}

1
2
3
4
5
6
7
8


## 2.2 String、StringBuilder、StringBuffer

### 2.2.1 String

字符串常量,不可更改,因为其内部定义的是一个final类型的数组来保存值的,如下:

private final char value[];

1
2
3
4
5
6
7
8
9
10

所以,当我们每次去“更改”String变量的值的时候(包括重新赋值或者使用String内部的一些方法),其实是重新新建了一个String对象(new String)来保存新的值,然后让我们的变量指向新的对象。因此,当我们需要频繁改变字符串的时候,使用String会带来较大的开销。

定义String的方法有两种:

(1)String str = "abc";
(2)String str2 = new String("def");

第一种方式创建的String对象“abc”是存放在字符串常量池中,创建过程是,首先在字符串常量池中查找有没有"abc"对象,如果有则将str直接指向它,如果没有就在字符串常量池中创建出来“abc”,然后在将str指向它。当有另一个String变量被赋值为abc时,直接将字符串常量池中的地址给它。如下:

String a = “abc”;
String b = “abc”;
System.out.println(a == b); //打印 true

1
2
3

也就是说通过第一种方式创建的字符串在字符串常量池中,是可共享的。同时,也是不可更改的,体现在:

String a = “abc”;
String b = “abc”;
b = b + “def”;

1
2
3
4
此时,字符串常量池中存在了两个对象“abc”和“abcdef”。

第二种创建方式其实分为两步:

String s = “def”;
String str2 = new String(s);

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

第一步就是上面的第一种情况;第二步在堆内存中new出一个String对象,将str2指向该堆内存地址,新new出的String对象内容,是在字符串常量池中找到的或创建出“def”对象,相当于此时存在两份“def”对象拷贝,一份存在字符串常量池中,一份被堆内存的String对象私有化管理着。所以使用String str2 = new String("def");这种方式创建对象,实际上创建了两个对象。

###2.2.2 StringBuffer(JDK 1.0) 和StringBuilder(JDK1.5)

StringBuffer和StringBuilder在功能上基本完全相同,它们都继承自AbstractStringBuilder,使用方法也都一样;可以对StringBuffer和StringBuilder对象进行改变,每次改变还是再原来的对象上发生的,不会重新new出新的StringBuffer或StringBuilder对象来。所以,当我们需要频繁修改字符串内容的时候,使用StringBuffer和StringBuilder是很好地选择。

StringBuffer和StringBuilder的最主要区别就是线程安全方面,由于在StringBuffer内大部分方法都添加了synchronized同步,所以StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此,当我们处于多线程的环境下时,我们需要使用StringBuffer,如果我们的程序是线程安全的使用StringBuilder在性能上就会更优一点。


历年面试题:

**String与StringBuffer的区别:**



## 2.3 关键字

### 2.3.1 switch

**1、switch中的参数类型**

在jdk1.7 之前switch 只能支持 byte、short、char、int或者其对应的封装类以及 Enum 类型。
在jdk1.7 及1.7以后,switch也支持了String类型,如下:

String str = “abc”;
switch (str) {
case “abc”:
System.out.println(“—–abc—–”);
break;
case “aaa”:
System.out.println(“—–aaa—–”);
break;
}

1
2
3

枚举类型

enum ColorEnum {
RED,
GREEN,
BLUE
}

public static void main(String[] args) {
ColorEnum color = ColorEnum.BLUE;
switch (color) {
case RED:
System.out.println(“红色”);
break;
case GREEN:
System.out.println(“绿色”);
break;
case BLUE:
System.out.println(“蓝色”);
break;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

**历年试题:**

**swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?**


### 2.3.2 break,continue,goto

break的作用是跳出当前循环块(for、while、do while)或程序块(switch)。在循环块中的作用是跳出当前正在循环的循环体。在程序块中的作用是中断和下一个case条件的比较。

continue用于结束循环体中其后语句的执行,并跳回循环程序块的开头执行下一次循环,而不是离开循环体。

goto:goto是java中的保留字,现在没有在java中使用。


**break,continue与标签**

“标签”是后面跟一个冒号的标识符,就象下面这样:```label1:```

对 Java 来说,唯一用到标签的地方是在循环语句之前。进一步说,它实际需要紧靠在循环语句的前方——在
标签和循环之间置入任何语句都是不明智的。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另
一个循环或者一个开关。这是由于 break 和 continue 关键字通常只中断当前循环,但若随同标签使用,它们
就会中断到存在标签的地方。如下所示:

label1:
外部循环{
内部循环 {
//…
break; //1
//…
continue; //2
//…
continue label1; //3
//…
break label1; //4
}
}

1
2
3
4
5
6
7
8
9
10
11
12
在条件 1 中,break 中断内部循环,并在外部循环结束。

在条件 2 中,continue 移回内部循环的起始处。

在条件 3 中,continue label1 却同时中断内部循环以及外部循环,并移至 label1 处。随后,它实际是继续
循环,但却从外部循环开始。

在条件 4 中,break label1 也会中断所有循环,并回到 label1 处,但并不重
新进入循环。也就是说,它实际是完全中止了两个循环。

**示例:**

public static void main(String[] args) {
outerCycle:
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++){
if(j == 5){
continue outerCycle;
}
System.out.print(“(“+i+”,”+j+”) “);
}
System.out.println();
}
}

1
2
3
4
5

给外层循起始处加了标签(也就是说给外层循环起了个名字)“outerCycle”,在内层循环使用“continue outerCycle”时,就相当于在outerCycle这个循环中直接使用continue语句。

(0,0) (0,1) (0,2) (0,3) (0,4) (1,0) (1,1) (1,2) (1,3) (1,4) (2,0) (2,1) (2,2) (2,3) (2,4) (3,0) (3,1) (3,2) (3,3) (3,4) (4,0) (4,1) (4,2) (4,3) (4,4) (5,0) (5,1) (5,2) (5,3) (5,4) (6,0) (6,1) (6,2) (6,3) (6,4) (7,0) (7,1) (7,2) (7,3) (7,4) (8,0) (8,1) (8,2) (8,3) (8,4) (9,0) (9,1) (9,2) (9,3) (9,4)

public static void main(String[] args) {
outerCycle:
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++){
if(j == 5){
break outerCycle;
}
System.out.print(“(“+i+”,”+j+”) “);
}
System.out.println();
}
}

1
2
3
4
5
6
7
8
9
10

运行结果

(0,0) (0,1) (0,2) (0,3) (0,4)


**历年试题:**

请写出下面程序的运行结果 :

public static void main(String[] args) {
int i = 0;
outer:
// 第一个标签
for (; true; ) {
inner:
//第二个标签
for (; i < 10; i++) {
prt(“i = “ + i);
if (i == 2) {
prt(“continue”);
continue;
}
if (i == 3) {
prt(“break”);
i++; // 注意
break;
}
if (i == 7) {
prt(“continue outer”);
i++; //注意
continue outer;
}
if (i == 8) {
prt(“break outer”);
break outer;
}
for (int k = 0; k < 5; k++) {
if (k == 3) {
prt(“continue inner”);
continue inner;
}
}
}
}

}

static void prt(String s) {
System.out.println(s);
}

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


下面是输出结果:
i = 0
continue inner
i = 1
continue inner
i = 2
continue
i = 3
break
i = 4
continue inner
i = 5
continue inner
i = 6
continue inner
i = 7
continue outer
i = 8
break outer



**需要匹配后通过break关键字跳出程序块,不然继续向下执行**

下面的方法,当输入为2的时候返回值是多少?()

public static int getValue(int i) {
    int result = 0;
    switch (i) {
    case 1:
        result = result + i;
    case 2:
        result = result + i * 2;
    case 3:
        result = result + i * 3;
    }
    return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

A0 B2 C4 D10


**break语句的描述中,正确的是( )**

A、只中断最内层的循环

B、只中断最外层的循环

C、借助于标号,可以实现任何外层循环中断

D、只中断某一层的循环


##2.4 值传递和引用传递

首先要说明的是java中是没有指针的,java中只存在值传递,只存在值传递!!! 然而我们经常看到对于对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是不要被这个假象所蒙蔽,实际上这个传入函数的值是对象引用的拷贝,即传递的是引用的地址值,所以还是按值传递。

需要注意的是当传递的是数组名或对象实例的话,其实传递的都是地址拷贝

public class Test3 {
public static void change(int a){
a=50;
}
public static void main(String[] args) {
int a=10;
System.out.println(a); //10
change(a);
System.out.println(a); //10
}
}

1
2


public class Test3 {
public static void change(int []a){
a[0]=50;
}
public static void main(String[] args) {
int []a={10,20};
System.out.println(a[0]); //10
change(a);
System.out.println(a[0]); //50
}
}

1
2


class Emp {
public int age;
}
public class Test {
public static void change(Emp emp)
{
emp.age = 50;
emp = new Emp();//再创建一个对象
emp.age=1000;
}

public static void main(String[] args) {
    Emp emp = new Emp();
    emp.age = 100;
    System.out.println(emp.age);  //100
    change(emp);
    System.out.println(emp.age);  //50
    System.out.println(emp.age);  //50
}

}

1
2
3
4
5
6


**历史考题**

指出下列程序运行的结果 ()

public class Example {

String str = new String("good");

char[] ch = { 'a', 'b', 'c' };

public static void main(String args[]) {

    Example ex = new Example();

    ex.change(ex.str, ex.ch);

    System.out.print(ex.str + " and ");  //good and gbc

    System.out.print(ex.ch);

}

public void change(String str, char ch[]) {

    str = "test ok";

    ch[0] = 'g';

}

}

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
A、 good and abc

B、 good and gbc

C、 test ok and abc

D、 test ok and gbc



##2.5 数组

**声明数组的两种方式:**

int a[];
int []a;

通过上边的定义,我们只是得到了一个数组的引用。这时已经为引用分配了存储空间,但是还没有给数组对象本身分配任何空间。想要给数组对象分配存储空间,必须使用初始化表达式。

**初始化数组的方式:**

int a[] = new int[8]; //分配空间,赋值默认值
int a[] = {0,1,2,3};
int a[] = new int[]{0,1,2,3};



**默认初始化**

如果我们利用int a[] = new int[6],为数组元素分配了空间,但没有初始化,Java会为他们分配默认值。如下表所示:

基本类型 默认值
boolean false
char '/u0000'(null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d


**7.下面哪些语句能够正确地生成5个空字符串?**

A.String a[]=new String[5];for(int i=0;i<5;a[i++]="");

B.String a[]={"","","","",""};

C.String a[5];

D.String[5] a;

E.String []a=new String[5];for(int i=0;i<5;a[i++]=null);



**5.下面哪个语句正确地声明一个整型的二维数组? **


A. int a[][] = new int[][];

B. int a[10][10] = new int[][];

C. int a[][] = new int[10][10];

D. int [][]a = new int[10][10];

E. int []a[] = new int[10][10];


**27.哪个语句创建了一个数组实例? **

A. int[] ia = new int [15];

B. float fa = new float [20];

C. char[] ca = "Some String";

D. int ia [][] = {4, 5, 6} {1, 2, 3};



**数组有没有length()这个方法? String有没有length()这个方法?**
没有,有

**请问下面程序在初始化后,a[0]和b[0]的值是多少?**

String []a = new String[10]; //null
int []b = new int[10]; //0



**下面程序的运行结果是:**

int index=1;
int foo[]=new int[3];
int bar=foo[index];
int baz=bar+index;

1
2
3
4
5
6
7
8
9

A. baz has a value of 0
B. baz has value of 1
C. baz has value of 2
D. an exception is thrown
E. the code will not compile

**下面代码的运行结果为:()**

import java.io.*;

import java.util.*;
public class foo{

public static void main (String[] args){

    String s;

    System.out.println("s=" + s);

}

}

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

A 代码得到编译,并输出“s=”

B 代码得到编译,并输出“s=null”

C 由于String s没有初始化,代码不能编译通过

D 代码得到编译,但捕获到 NullPointException异常


##2.6 构造函数相关

**1、如果一个类中没有写任何的构造方法,JVM会生成一个默认的无参构造方法。**

**2、如果一个基类中写了有参构造函数,没有定义无参构造函数,基类是不会默认生成无参构造函数的。而且子类的构造函数中,如果没有显示通过super.调用基类构造函数,那么默认是调用父类的无参构造方法(即默认为super(),一般这句话省略了)。**

下面哪三个描述是正确的? B、C、E

A. 默认构造器初始化方法变量

B. 默认构造器有和它所在类相同的访问修饰词.

C. 默认构造器调用其父类的无参构造器.

D. 如果一个类没有无参构造器,编译器会它创建一个默认构造器.

E. 只有当一个类没有任何构造器时,编译器会为它创建一个默认构造器


以下子类B的情形哪一个是可以通过编译的:

public class A {
public A(String s){ }
}

public class B extends A {
String name = “llyB”;
}

public class B extends A {
String name = “llyB”;
public B(String s){}
}

public class B extends A {
String name = “llyB”;
public B(String s){
super(s);
}
} 能

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

所以,只要记住,在子类的构造方法中,只要里面没有显示的通过super去调用父类相应的构造方法,默认都是调用super(),即无参构造方法,因此要确保父类有相应的构造方法。


**3、Java创建对象的几种方式:**

(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。

(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。



历年考题:不通过构造函数也能创建对象吗(A)

A 是 B 否



下列说法正确的有(c)

A. class中的constructor不可省略

B. constructor必须与class同名,但方法不能与class同名

C. constructor在一个对象被new时执行

D.一个class只能定义一个constructor


**4、构造函数调用顺序**

在Java中,子类的构造过程中必须调用其父类的构造函数,是因为有继承关系存在时,子类要把父类的内容继承下来。但如果父类有多个构造函数时,该如何选择调用呢?

第一个规则:子类的构造过程中,必须调用其父类的构造方法。一个类,如果我们不写构造方法,那么编译器会帮我们加上一个默认的构造方法(就是没有参数的构造方法),但是如果你自己写了构造方法,那么编译器就不会给你添加了,所以有时候当你new一个子类对象的时候,肯定调用了子类的构造方法,但是如果在子类构造方法中我们并没有显示的调用基类的构造方法,如:super(); 这样就会调用父类没有参数的构造方法。

第二个规则:如果子类的构造方法中既没有显示的调用基类构造方法,而基类中又没有无参的构造方法,则编译出错,所以,通常我们需要显示的:super(参数列表),来调用父类有参数的构造函数,此时无参的构造函数就不会被调用。

总之,一句话:子类没有显示调用父类构造函数,不管子类构造函数是否带参数都默认调用父类无参的构造函数,若父类没有则编译出错。

历年考题:下面是People和Child类的定义和构造方法,每个构造方法都输出编号。在执行new Child("mike")的时候都有哪些构造方法被顺序调用?请选择输出结果 ( )

class People {
String name;

public People() {
    System.out.print(1);
}

public People(String name) {
    System.out.print(2);
    this.name = name;
}

}

class Child extends People {
People father;

public Child(String name) {
    System.out.print(3);
    this.name = name;
    father = new People(name + ":F");
}

public Child() {
    System.out.print(4);
}

}

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
A312              B 32               C 432              D 132


**5、override与Overloading**

Override(重写):
在子类中定义与父类具有完全相同的名称和参数的方法,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,是子类与父类之间多态性的一种体现。特点如下:
(1)子类方法的访问权限只能比父类的更大,不能更小(可以相同);
(2)如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法;

Overload(重载):
同一个类中可以有多个名称相同的方法,但方法的参数个数和参数类型或者参数顺序不同;
关于重载函数返回类型能否不一样,需分情况:
(1)如果几个Overloaded的方法的参数列表不一样(个数或类型),它们的返回者类型当然也可以不一样;
(2)两个方法的参数列表完全一样,则不能通过让其返回类型的不同来实现重载。
(3)不同的参数顺序也是可以实现重载的;
(4)构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载。

### 2.7 静态代码块执行顺序

执行顺序:1.静态代码块 --> 2.普通代码块 --> 3.构造方法

需要明白的是,1是类级别的,2和3是实例级别的,所以在父子类关系中,上述的执行顺序为:

父类静态代码块-->子类静态代码块-->父类普通代码块-->父类构造方法-->子类代码块-->子类构造方法;

也就是上到下(父类到子类)先走完 类级别的(静态的)--> 再依次走完父类的所有实例级别代码 --> 再走子类所有实例级别代码

历史题目:

如下代码的输出结果:

public class HelloB extends HelloA {
public HelloB() {
System.out.println(“———–HelloB 构造方法————“);
}

{
    System.out.println("I’m B class");
}

static{
    System.out.println("static B");
}
public static void main(String[] args){
    new HelloB();
}

}

class HelloA{
public HelloA(){
System.out.println(“———–HelloA 构造方法————“);
}

{
    System.out.println("I’m A class");
}

static{
    System.out.println("static A");
}

}

1
2
3
4
5
6
7
8
9
10
11
12

输出结果:
static A
static B
I’m A class
-----------HelloA 构造方法------------
I’m B class
-----------HelloB 构造方法------------


**下面程序的运行结果是什么()**

class HelloA {

public HelloA() {
    System.out.println("HelloA");
}

{ System.out.println("I'm A class"); }

static { System.out.println("static A"); }

}

public class HelloB extends HelloA {
public HelloB() {
System.out.println(“HelloB”);
}

{ System.out.println("I'm B class"); }

static { System.out.println("static B"); }

public static void main(String[] args) { 

     new HelloB();
   }

}

1
2
3

答案:

static A
static B
I’m A class
HelloA
I’m B class
HelloB

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


### 2.8 面向对象

1、Java面向对象的基本思想之一是封装细节并且公开接口。Java语言采用访问控制修饰符来控制类及类的方法和变量的访问权限,从而向使用者暴露接口,但隐藏实现细节。访问控制分为四种级别:

(1)public: 用public修饰的类、类属变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问;

(2)protected: 用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问(此处稍后解释),protected重点突出继承;

(3)default: 如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出包;

(4)private: 用private修饰的类、类属变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。 

|修饰符 |当前类 |同 包 |子 类 |其他包|
| ----- |------ |-------|-------|------|
|public | √ | √ | √ | √ |
|protected | √ | √ | √ | ×|
|default| √ | √ | × | × |
|private| √ | × | × | × |

2、final修饰的类不能被继承,没有子类。

3、abstract修饰的类不能被实例化,必须被子类继承。类只要有一个抽象方法就必定是抽象类,但抽象类不一定要有抽象方法。声明方式必须是:```abstract void C();``` 不能带我实现体大括号。

历史题目:

**1、如下代码:**

class A {

A() { }

}

class B extends A {

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

哪两个说明是正确的?

A. B类的构造器应该是 public.

B. B类的构造器应该是没有参数

C. B类的构造器应该调用this().

D. B类的构造器应该调用super().

**是否可以继承String类?**

String类是final类故不可以继承。

**以public修饰的类如:public class Car{…} 则Car( ) **

A、可被其它程序包中的类使用 B、仅能被本程序包中的类使用

C、不能被任意其它类使用 D、不能被其它类继承

### 2.9 equals与==的区别

**(1)==是一个运算符,它比较的是值**

对于基本数据类型,直接比较其数据值是否相等。如果是不同的基本数据类型之间进行比较,则遵循基本数据类型间运算的转换原则。如下:

```
if(12 == 12.0){
System.out.println("-----12 == 12.0-------");
}

此时打印了—–12 == 12.0——-,因为低一级的int类型的12自动转换为高一级的float类型

对于引用类型,==比较的还是值,只不过此时比较的是两个对象变量的内存地址。所以,用==来比较对象,实际上是判断这两个对象是否是同一个new出来的对象,或者是否是一个对象赋值给另一个对象的情况。如:

1
2
String s1 = new String("abc");
String s2 = s1;//将s1对的内存地址赋给了s2,此时s1==s2返回true;

(2)equals

equals方法是属于Object类的一个方法,其实现源码如下:

1
2
3
public boolean equals(Object obj) {  
return (this == obj);
}

可以看到,其实equals方法里面用的还是==运算符,所以对于那些没有重写过Object类的equals方法来说,==和equals方法是等价的!

然而,很多类都自己去重写了equals方法,比如String类、所有基本数据类型的包装类等
String类的equals源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {  
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

首先判断是否是同一个new出来的对象,即判断内存地址是否相同;如果不同则判断对象中的内容是否相同。

Integer类的equals方法如下:

1
2
3
4
5
6
public boolean equals(Object obj) {  
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

直接转成判断值是否相等了。

因此,对于String类和所有基本数据类型的包装类来说,equals方法就是判断其内容是否相等。对于其他类来说,要具体看其是否重写了equals方法及具体业务实现。

另:对于基本数据类型来说,使用equals方法,需要用该基本类型对应的包装类,因为equals是针对对象来使用的!

下面程序的运行结果是()

1
2
3
String str1 = "hello";
String str2 = "he" + new String("llo");
System.err.println(str1 == str2);

##其它基础

&和&&的区别

&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and)。

*下面的语句会产生什么样的输出? *

System.out.println(4&7);

A.4

B.5

C.6

D.7

E.0

5、try、catch、finally执行顺序问题

14. 如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Foo {

public static void main(String[] args) {

try {

return;

} finally {

System.out.println( "Finally" );

}
}
}

输出结果是什么? A

A. Finally

B.编译失败

C. 代码正常运行但没有任何输出.

D. 运行时抛出异常

下面函数将返回?

1
2
3
4
5
6
7
8
9
publicstaticintfunc (){
try{
return 1;
}catch(Exception e){
return 2;
}finally{
return 3;
}
}

A、1 B、2 C、3 D、编译错误

什么是线性表

线性表就是由n(n>0)个数据元素a1,a2….组成的有限序列

  • 数据元素的个数为n,表长度,当n=0时为空表
  • 一个线性表为非空表,既n>0,则可以简单记为(a1,a2….,an)
  • 数据元素ai表示了各个元素,在不同场合含义不同
    逻辑结构特点
  • 有且仅有一个开始结点a1,没有直接前驱结点,有且仅有一个后继结点a2
  • 有且仅有一个终结点an,没有直接后继结点,有且仅有一个前驱结点an-1
  • 其余内部结点ai(2<=i<=n-1)都有且仅有一个前驱结点和一个后继结点
  • 同一个线性表,各个数据元素ai必须具有相同的数据类型

线性表的基本运算

  1. 初始化
  2. 计算表表长
  3. 获取结点
  4. 查找结点
  5. 插入结点
  6. 删除结点

顺序表

  1. 顺序表就是按照顺序存储方式存储的线性表,该线性表的结点按照逻辑次依次存放在计算机的一组连续的存储单元中
  2. 结点ai存储地址的LOC(ai)计算式:LOC(ai)=a1+(i-1)*c (0<=i<=n)
  3. 重点
    • 插入结点:先判断结点数量是否已满,以及插入结点序号是否正确,满足,将顺序表的数据后移,同时插入节点,更新结点数量表长度
    • 删除结点:先判断待删除结点序号是否正确,然后开始移动数据,并更新结点数量
  4. 顺序表缺点
    • 在插入和删除节点的时候,往往需要移动大量的数据
    • 如果表比较大,有时比较难分配足够的连续存储空间,往往导致内存分配失败,而无法存储

链表结构

  1. 链表结构是一种动态存储分配的结构形式,可以根据需要动态申请内存单元
  2. 链表中每个结点都包含如下两部分
    • 数据部分,保存的是该节点的实际数据
    • 地址部分,保存的是下一个节点的地址
  3. 采用引用来指示下一个数据的地址,因此在链表结构中,逻辑上相邻的结点在内存中并不一定相邻,逻辑上相邻关系通过地址部分的引用变量来实现。
  4. 链表结构分类
    • 单链表
    • 双链表:每个结点包含两个引用,一个指向下一个结点,一个指向上一个结点
    • 单循环链表:在单链表中,将终端结点的引用域null改为指向表头结点或者开始结点
    • 多重链的循环表:将表中结点链在多个环上
  5. 重点
    • 追加结点
    • 头引用head

什么是数据结构

  • 数据结构是数据对象、存在于该对象的实例以及组成实例的数据元素之间的关系,并且这种关系可以通过定义相关的函数来给出
  • 数据结构是抽象数据类型ADT的物理实现
  • 一个数据结构是由数据元素依据某种逻辑联系起来的,对数据元素之间逻辑关系的描述称为数据的逻辑结构。由于数据必须在计算机内存储,数据的存储结构是其在计算机内的表示,既数据的实现形式

基本概念

  • 数据(Data):数据是信息的载体,其能够被计算机识别、存储和加工处理,是计算机程序加工的“原材料”。
  • 数据元素:数据元素是数据的基本单位,其也称之为元素,结点、顶点、记录等。
  • 数据项:一个元素可以由若干个数据项组成,数据项是具有独立含义的最小标识单位。数据项也可称之为字段、域、属性
  • 数据结构:是指数据间的相互关系,也就是数据的组织形式。

####数据结构的内容

  • 数据的逻辑结构
  • 数据的存储结构
  • 数据的运算(检索、插入、删除、更新、排序等)

数据结构分类

  • 按逻辑结构分类
    1. 线性结构
      • 线性结构是非空集
      • 线性结构有且仅有一个开始结点和一个终端结点
      • 线性结构所有结点最多只有一个直接前驱结点和一个直接后继结点
      • 线性表、栈、队列和串等
    2. 非线性结构
      • 非线性结构是非空集
      • 非线性结构的一个结点可能有多个直接前驱结点和多个直接后继结点
      • 数组、广义表、树结构和图结构等
  • 按存储结构分类
    1. 顺序存储结构
      • 在一块连续的存储区域一个接着一个的存放数据
      • 线性存储方式主要用于线性逻辑结构的数据存放
    2. 链式存储结构
      • 不要求逻辑上相邻的结点物理位置上相邻,结点间的逻辑关系由附加的引用字段表示。一个结点的引用字段往往指向下一个结点的存放位置。
      • 也称链式存储结构
    3. 索引存储结构
      • 采用附加的索引表的方式来存储结点信息
    4. 散列存储方式
      • 根据结点的关键字 直接计算出该结点的存储地址的一种存储方式

常用数据结构

  1. 数组
    数组是一种聚合数据类型,是将具有相同类型的若干变量有序的组织在一起的计集合。

  2. 栈是一种特殊的线性表,其只能在一个表的固定端进行数据节点的插入和删除操作。
  3. 队列
    队列和栈类似也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,在表的另一端进行删除操作。
  4. 链表
    链表是一种数据元素按照链式存储结构进行存储的数据结构。

  5. 树是典型的非线性结构,其中包括了n个结点的有穷集合。

  6. 图是另一种非线性结构

  7. 堆是一种特殊的树形结构,我们一般讨论的是堆都是二叉树。
  8. 散列表
    散列表源自于散列函数,结构中存在关键字和T相等的记录。

算法:

  • 算法是模型分析的一组可行的、确定的和有穷的规则(广泛)
  • 算法是解决实际问题的一种精确的描述方法
  • 算法是对特定问题的求解步骤的一种精确描述方法

算法的特性

  • 有穷性:算法的指令或者执行步骤是有限的
  • 确切的:算法的每一个指令和步骤都必须有明确的定义和描述
  • 输入:一个算法应该有的输入条件,用于初始化
  • 输出:一个算法应该有个名明确的结果输出
  • 可行性:执行步骤的可行,且有可以在有限时间内完成

算法分类

  • 按照应用来分类
    • 基础算法、数据结构、几何、加密、查询、排序等等
  • 按照确定性来分类
    • 确定性算法:有限时间内完成,结果唯一
    • 非确定性算法:有限时间内完成,结果往往不唯一
  • 按照算法思路
    • 递推、递归、穷举、贪婪、分治、动态规划、迭代等算法

算法概念

  • 数据结构+算法+程序设计语言 = 程序
  • 数据结构表示处理对象,算法是计算和处理的核心方法,程序设计语言是算法的实现方法。这几者综合便构成了一个程序

算法的表示

  • 自然语言表示
  • 流程图
  • N-S图
  • 伪代码表示

算法性能评价

  • 时间复杂度:算法执行所消耗的时间,时间越短,性能越好,算法越好。
    • 与每条语句执行的数量有关
    • 与问题的规模有关
  • 空间复杂度:算法执行所消耗的存储空间,消耗越小,算法越好。
    • 程序保存所需要的存储空间,程序的大小
    • 程序执行过程中所需消耗的存储空间(变量等)

算法实例

一个班级学生档案集中查找某一个学生的档案
伪代码

1
2
3
4
5
6
7
8
9
10
变量 x = 输入需要查找的数据
变量 arr = 随机生产数组数据
for 1到20{
if(arr[i] ==){
找到数据
break;
}
}
输出该数据的位置
程序结束

Code

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
public Class Test1{
static int N = 20;
public static void main(String args[]){
int arr[] = new int[N];
int x;
int pos = -1;
Random r= new Random();
for(int i=0; i<N; i++){
arr[i] = r.nextInt(100);
}
System.out.println("生成随机数序列")
for(int i=0; i<N; i++){
System.out.print(arr[i]+" ");
}

System.out.print("输入要查找的整数");
Scanner input = new Scanner(System.in);
x = input.getInt();
for(int i=0; i<N; i++){
if(arr[i] == x){
pos = i;
break;
}
}

if(f<0){
System.out.print("未找到数据");
}else{
System.out.print(x+"数据位于"+pos+"位置,是数组的第"+(pos+1)+"个元素");
}
}
}

三方登录实多平台快捷方现方式

Mob平台 集成开发 三方登录和分享
ShareSdk.xml 配置
AndroidManifest.xml 配置

注意点

  • 微信平台
    包名千万不要写错
    数字签名 是MD5 非 SHA1(大部分平台都是SHA1)
    测试签名和发行签名区分或统一。
  • QQ 平台
    添加测试账号

数据传输加密

  在开发应用过程中,客户端与服务端经常需要进行数据传输,涉及到重要隐私信息时,开发者自然会想到对其进行加密,即使传输过程中被“有心人”截取,也不会将信息泄露。对于加密算法,相信不少开发者也有所耳闻,比如MD5加密,Base64加密,DES加密,AES加密,RSA加密等等。在这里我主要向大家介绍一下我在开发过程中使用到的加密算法,RSA加密算法+AES加密算法。简单地介绍一下这两种算法吧。

RSA

  之所以叫RSA算法,是因为算法的三位发明者RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准,主要的算法原理就不多加介绍,如果对此感兴趣的话,建议去百度一下RSA算法。需要了解的是RSA算法属于非对称加密算法,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。简单的说是“公钥加密,私钥解密;私钥加密,公钥解密”。

AES

   高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

为什么要结合使用这两种算法

  如果不清楚非对称算法和对称算法,也许你会问,为什么要结合使用这两种算法,单纯使用一种算法不行吗?这就要结合不同的场景和需求了。

  客户端传输重要信息给服务端,服务端返回的信息不需加密的情况
  客户端传输重要信息给服务端,服务端返回的信息不需加密,例如绑定银行卡的时候,需要传递用户的银行卡号,手机号等重要信息,客户端这边就需要对这些重要信息进行加密,使用RSA公钥加密,服务端使用RSA解密,然后返回一些普通信息,比如状态码code,提示信息msg,提示操作是成功还是失败。这种场景下,仅仅使用RSA加密是可以的。

  客户端传输重要信息给服务端,服务端返回的信息需加密的情况
  客户端传输重要信息给服务端,服务端返回的信息需加密,例如客户端登录的时候,传递用户名和密码等资料,需要进行加密,服务端验证登录信息后,返回令牌token需要进行加密,客户端解密后保存。此时就需要结合这两种算法了。至于整个流程是怎样的,在下面会慢慢通过例子向你介绍,因为如果一开始就这么多文字类的操作,可能会让读者感到一头雾水。

使用RSA加密和解密

产生公钥和私钥:产生RSA公钥和密钥的方法有很多,在这里我直接使用我封装好的方法产生,都最后我会将两个算法的工具类赠送给大家。

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
/**
* 生成公钥和私钥
*
* @throws Exception
*
*/
public static void getKeys() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

String publicKeyStr = getPublicKeyStr(publicKey);
String privateKeyStr = getPrivateKeyStr(privateKey);

System.out.println("公钥\r\n" + publicKeyStr);
System.out.println("私钥\r\n" + privateKeyStr);
}

public static String getPrivateKeyStr(PrivateKey privateKey)
throws Exception {
return new String(Base64Utils.encode(privateKey.getEncoded()));
}

public static String getPublicKeyStr(PublicKey publicKey) throws Exception {
return new String(Base64Utils.encode(publicKey.getEncoded()));
}

公匙

1
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRQZ5O/AOAjeYAaSFf6Rjhqovws78I716I9oGF7WxCIPmcaUa1YuyLOncCCuPsaw69+RMWjdbOBp8hd4PPM/d4mKTOVEYUE0SfxhhDTZaM5CzQEUXUyXy7icQTGR5wBjrbjU1yHCKOf5PJJZZQWB06husSFZ40TdL7FdlBpZ1u1QIDAQAB

私钥

1
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJFBnk78A4CN5gBpIV/pGOGqi/CzvwjvXoj2gYXtbEIg+ZxpRrVi7Is6dwIK4+xrDr35ExaN1s4GnyF3g88z93iYpM5URhQTRJ/GGENNlozkLNARRdTJfLuJxBMZHnAGOtuNTXIcIo5/k8klllBYHTqG6xIVnjRN0vsV2UGlnW7VAgMBAAECgYBMoT9xD8aRNUrXgJ7YyFIWCzEUZN8tSYqn2tPt4ZkxMdA9UdS5sFx1/vv1meUwPjJiylnlliJyQlAFCdYBo7qzmib8+3Q8EU3MDP9bNlpxxC1go57/q/TbaymWyOk3pK2VXaX+8vQmllgRZMQRi2JFBHVoep1f1x7lSsf2TpipgQJBANJlO+UDmync9X/1YdrVaDOi4o7g3w9u1eVq9B01+WklAP3bvxIoBRI97HlDPKHx+CZXeODx1xj0xPOK3HUz5FECQQCwvdagPPtWHhHx0boPF/s4ZrTUIH04afuePUuwKTQQRijnl0eb2idBe0z2VAH1utPps/p4SpuT3HI3PJJ8MlVFAkAFypuXdj3zLQ3k89A5wd4Ybcdmv3HkbtyccBFALJgs+MPKOR5NVaSuF95GiD9HBe4awBWnu4B8Q2CYg54F6+PBAkBKNgvukGyARnQGc6eKOumTTxzSjSnHDElIsjgbqdFgm/UE+TJqMHmXNyyjqbaA9YeRc67R35HfzgpvQxHG8GN5AkEAxSKOlfACUCQ/CZJovETMmaUDas463hbrUznp71uRMk8RP7DY/lBnGGMeUeeZLIVK5X2Ngcp9nJQSKWCGtpnfLQ==

  很明显,公钥字符串长度比较短,私钥的比较长。生成完密钥后,公钥可以存放在客户端,即使被别人知道公钥,也是没有问题的;私钥则一定要保存在服务端。如果到时公司面临人事变动,避免私钥被离职人员泄露,可以重新生成公钥和密钥。

使用公钥加密,私钥解密
Alt text
这里在客户端模拟加密的情况,对字符串”Beyond黄家驹”使用RSA加密,调用RSAUtils的encryptByPublicKey()方法,输出结果为:

1
密文: BRFjf3tUqRqlwuP5JtzxZinf7lp+AHuHM9JSabM5BNFDxuUe9+uuO6RpCHVH5PibifqQHzGNsyZn1G9QcIENT9Tbm+PZwAbNUlMPZRDBU1FSnOtY8dBdeW/lJdnY9sJVwNvIBnOLQk66hxRh6R2149dwlgdsGUpWMOMBzcP3vsU=

在服务端,可以使用RSAUtils的decryptByPrivateKey()方法进行解密,现在模拟服务端解密
Alt text
  在这里虽然没有完全模拟数据传输过程,比如说客户端发起一个网络请求,传递参数给服务端,服务端接收参数并进行处理,也是为了让大家可以更加容易明白,所以这里只是进行简单的模拟。可以看到Android客户端端和Java服务端的RSA加密解密算法是可以互通的,原因是他们所使用到的base64加密类是一致的,所以才可以实现加密和解密的算法互通。
Alt text
Alt text
  使用到的jar包都是javabase64-1.3.1.jar,相信不少人都知道,java中有自带的Base64算法类,但是安卓中却没有,之前出现的情况是,使用的Base64类不统一,比如在安卓客户端开发使用的Base64算法是使用第三方提供的jar包,而java服务端中使用的是JDK自带的Base64,导致从客户端传过来的密文,服务端解析出错。

  上面的例子展示了客户端使用公钥加密,服务端使用私钥解密的过程。也许你会这么想,既然可以如此,那服务端那边信息也可以通过RSA加密后,传递加密信息过来,客户端进行解密。但是,这样做,显示是不安全的。原因是,由于客户端并没有保存私钥,只有公钥,只可以服务端进行私钥加密,客户端进行公钥解密,但由于公钥是公开,别人也可以获取到公钥,如果信息被他们截取,他们同样可以通过公钥进行解密,那么这样子加密,就毫无意义了,所以这个时候,就要结合对称算法,实现客户端与服务端之前的安全通信了。

使用AES加密解密
加密
Alt text
模拟客户端进行AES加密,我们通过调用AESUtils中的generateKey()方法,随机产生一个密钥,用于对数据进行加密。输出的结果为:

1
2
密钥: 6446c69c0f914a57
密文: GECDQOsc22yV48hdJENTMg==

解密
  模拟服务端进行AES解密,由于AES属于对称算法,加密和解密需要使用同一把密钥,所以,服务端要解密传递过来的内容,就需要密钥 + 密文。这里模拟一下服务端解密。
Alt text
  到这里也许你会问,客户端使用AES进行加密,服务端要进行解密的话,需要用到产生的密钥,那密钥必须从客户端传输到服务端,如果不对密钥进行加密,那加密就没有意义了。所以这里终于谈到了重点,RSA算法+AES算法结合使用。

RSA算法+AES算法的使用
  举一个简单的例子来说明一下吧,例如实名认证功能,需要传递用户真实姓名和身份证号,对于这种重要信息,需要进行加密处理。

客户端使用RSA + AES对重要信息进行加密
客户端加密过程主要分为以下三个步骤:

  1. 客户端随机产生AES的密钥;

  2. 对身份证信息(重要信息)进行AES加密;

  3. 通过使用RSA对AES密钥进行公钥加密。
    Alt text

  这样在传输的过程中,即时加密后的AES密钥被别人截取,对其也无济于事,因为他并不知道RSA的私钥,无法解密得到原本的AES密钥,就无法解密用AES加密后的重要信息。

服务端使用RSA + AES对重要信息进行解密

服务端解密过程主要分为以下两个步骤:

  1. 对加密后的AES密钥进行RSA私钥解密,拿到密钥原文;

  2. 对加密后的重要信息进行AES解密,拿到原始内容。
    Alt text

  现实开发中,服务端有时也需要向客户端传递重要信息,比如登录的时候,返回token给客户端,作为令牌,这个令牌就需要进行加密,原理也是差不多的,比上面多一个步骤而已,就是将解密后的AES密钥,对将要传递给客户端的数据token进行AES加密,返回给客户端,由于客户端和服务端都已经拿到同一把AES钥匙,所以客户端可以解密服务端返回的加密后的数据。如果客户端想要将令牌进行保存,则需要使用自己定义的默认的AES密钥进行加密后保存,需要使用的时候传入默认密钥和密文,解密后得到原token。

  上面提及到客户端加密,服务端返回数据不加密的情况,上面说到仅仅使用RSA是可以,但是还是建议同时使用这两种算法,即产生一个AES密钥,使用RSA对该密钥进行公钥加密,对重要信息进行AES加密,服务端通过RSA私钥解密拿到AES密钥,再对加密后的重要信息进行解密。如果仅仅使用RSA,服务端只通过RSA解密,这样会对于性能会有所影响,原因是RSA的解密耗时约等于AES解密耗时的100倍,所以如果每个重要信息都只通过RSA加密和解密,则会影响服务端系统的性能,所以建议两种算法一起使用。

同时还有相应的JS版RSA和AES算法,使用方式也差不多,在这里简单演示一下:

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
<!DOCTYPE html>
<html>
<head>
<title>RSA+AES.html</title>

<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="./js/rsa.js"></script>
<script type="text/javascript" src="./js/aes.js"></script>
<script type="text/javascript">
var key = getKey();//随机产生AES密钥
var encryptKey = RSA(key);//对AES密钥进行RSA加密
console.log("encryptKey: " + encryptKey);

//测试AES加密和解密
var cipherText = AESEnc(key,"123456");
var plainText = AESDec(key,cipherText);
console.log("密文: " + cipherText);
console.log("明文: " + plainText);
</script>
</head>

<body>
This is my HTML page. <br>
</body>
</html>

打开页面后,查看控制台输出:
Alt text
同时,模拟服务端解密,运行结果如下:
Alt text

需要注意的是:

1.RSAUtils中配置公钥和密钥,可以使用getKeys()方法产生。如果是客户端,则无须配置私钥,把没有私钥的RSAUtils放到客户端,因为仅需要用到公钥加密的方法。

2.AESUtils中配置偏移量IV_STRING;

3.rsa.js中最底部配置公钥,须和上面RSAUtils配置的公钥一致;

4.aes.js中的底部var iv = CryptoJS.enc.Utf8.parse(“16-Bytes–String”); //加密向量中,替换里面的字符串,加密向量须和
是上面的AESUtils中的偏移量一致。

各种语言的加密的处理方式有所差异,所以我们需要因地制宜。了解此加密的思想方法即可

  1. php 和 java RSA 对称加密互通的问题
  2. php与java通用AES加密解密算法
  3. Android错误解决:java.lang.NoSuchMethodError: No static method encodeBase64String

Android Studio修改包名

  1. 修改包显示方式

Alt text

通过修改包显示方式,我们可以更方便的修改包名。

  1. 修改包名

Alt text

  1. 改build.gradle(module:APP名字)文件中的 applicationId

Alt text

  1. clean和rebuild project

Alt text

  1. 关闭android studio重启(可选)

亲测有效,完美~

简易操作,全局替换

ctr+shift+r

Touch事件传递机制

其中Activity和View控件(TextView)拥有分派和处理事件方法,View容器(LinearLayout)具有分派,拦截,处理事件方法。这里也有个比喻:领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。

1
2
3
4
5
6
//分派事件
public boolean dispatchTouchEvent(MotionEvent ev)
//拦截事件
public boolean onInterceptTouchEvent(MotionEvent ev)
//处理事件
public boolean onTouchEvent(MotionEvent event)

Alt text

Alt text

从上面两张事件传递图我们的出来一些结论:

  1. 事件是先有dispatchTouchEvent分派给下一级

  2. 要经过onInterceptTouchEvent是否需要拦截,不拦截传递给下一级,最终传递给view控件,

  3. onTouchEvent方法中,在处理事件中,如果返回True,则表示能处理,传递将会终止。反着,不能,如果不能的话,这会返回上一级的onTouchEvent方法中,如果还是false,会一直到到上一层的onTouchEvent方法中。

好奇的我们会发现一个问题:图二中的黄色线,没有像红色线那样,先传递到最底层,然后再回滚回去,这是为什么呢?

答:就如我们故事中,他都知道了,底下人都不会去做了,那么他干嘛还分派给他呢,他就会自己做了,直接给onTouchEvent ,这就是事件传递中的“记忆”功能。我们是手指点击蓝色区域,Touch事件有两个,第一个ACTION_DOWN,第二个ACTION_UP,第一个 ACTION_DOWN事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知 道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent来处理,那就说明下面的View都没能成功处理该事件);当第二个 ACTION_UP事件向下传递到该View,该View的dispatchTouchEvent方法机会判断,若上次的事件由下面的view成功处理 了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的 onTouchEvent方法来处理该事件。

PS:关于这“记忆”功能的信息只在一系列事件完成之前有效,也就是从ACTION_DOWN事件开始,直到后续事件 ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了 (onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父 View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。

补充说明:

-》若在向下传递的过程中被拦截了,即onInterceptTouchEvent方法返回true,则事件将停止向下传递,直接由当前的onTouchEvent方法来处理,若处理成功则OK,若处理不成功,则事件会向上传递。

0%