目录
1)关于使用new String(b, 0, len);构造方法的原因
2)file.delete() -->只能删除空文件夹或单个文件
1.判断一个输入数字的类型
之前参加的笔试考了一个很简单的问题:判断输入一个整数是否是3的倍数。当时因为有一段时间没看Java基础的内容,都忘了还有hasNextInt()这种函数来判断整数类型。在百度走了一圈,选择了一种try-catch的方法。
public static void main(String[] args) {
int i = 0;
Scanner in = new Scanner(System.in);
System.out.println("请输入一个整数:");
while(true) {
try {
i = in.nextInt();
break;
}
catch(Exception ex) {
System.out.println("您输入的不是一个整数!请重新输入一个整数:");
in = new Scanner(System.in);
}
}
in.close();
}
在调试代码过程中我才发现,如果循环的条件为true,我需要为每一次输入的数字new一个新的对象,我感觉挺占用资源的,但是不知道怎么解决这个问题,可能用BufferedReader会好一些?这个还没好好学到,留待以后解决吧。
如果循环条件是hasNextInt(),代码如下:
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
double sum=0;
int m=0;
while(reader.hasNextDouble()) {
double x = reader.nextDouble();
m++;
sum+=x;
}
System.out.printf("%d个数的和为%f%n", m,sum);
System.out.printf("%d个数的平均值是%f", m,sum/m);
}
这个new一个对象就可以了,以后有时间可以研究一下这个函数的运行机制。
第三种方法跟第一种差不多,但没有用try-catch:
public static void main(String[] agrs) {
int i = 0;
Scanner in = new Scanner(System.in);
System.out.println("请输入一个整数:");
while(true) {
if(in.hasNextInt()) {
i = in.nextInt();
break;
}
else {
System.out.println("您输入的不是一个整数!请重新输入一个整数!");
in = new Scanner(System.in);
}
}
if(i%3==0) {
System.out.println(i+"是3的倍数");
}
else {
System.out.println(i+"不是3的倍数");
}
in.close();
}
这样来看,不如第二种方法好,但尝试了一下,好像也没法将第二种和第三种方法结合在一起……
2.Java的数组是动态长度的
public static void main(String[] agrs) {
int size = 5;
double[] number = new double[size];
}
3.Java的变量初始化问题
以前有一个印象是Java的变量需要经过初始化后才能使用,这种说法是不对的。应该是:Java中的局部变量(定义在方法内)必须初始化后才能使用。
我尝试了一下,如果定义在class内部,以static的修饰符进行限定,则可以在函数内(main)直接使用变量的默认值。
public class test {
public static boolean a;
public static void main(String[] agrs) {
System.out.println(a);
}
}
boolean的默认值是false,打印出来的结果也是false。(以下是各类型的默认值)
byte -->0 short -->0 int -->0 long -->0
char -->(一个空心方块)
float -->0.0 double -->0.0
boolean -->false
4.instanceof和引用数据类型的联系
我想要判断一个数的类型是否是int,于是我打算使用Java中的instanceof方法来进行判断。但是instanceof是对有继承关系的对象和类进行判断,而int是Java中的基础类型数据,不能直接使用这种方式进行判断。
在网上搜索到了两种方法:
1)前述(1.)的hasNextInt,nextInt等等是一种方法,但是针对的是输入流
2)
public static void main(String[] args){
test t=new test(); //main所在的类是test
int int_num=0;
double double_num=0;
System.out.println(t.judgeType(int_num));
System.out.println(t.judgeType(double_num));
}
public boolean judgeType(Object temp){ //我怀疑在此处Object
if(temp instanceof Integer) //对传入的int类型的变量进行
return true; //类型转换,int-->Integer
else
return false;
}
用注意Java中int是基础类型而Integer是引用类型,Integer的功能应用比int更强大。在网上找了一篇int和Integer的对比文章:
http://blog.csdn.net/maxracer/article/details/7967486
Integer和int是可以相互转化的,其他类型同理:
Integer i = new Integer(10);
Int value = i.intValue();
5.关于数组
Java中没有指针的概念,这一点和C不同。在Java中,数组是引用类型,其实就是应用了指针。数组中存放的是地址,地址指向存储空间中的值。
在做一道题的时候,发现String类型的数组可以由两个String类型的数组相加得到。
String[] suit = {"梅花","方块","红桃","黑桃"};
String[] number = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
String[] card = new String[54];
card[52] = "大王";
card[53] = "小王";
int num = 0;
for(int i=0; i<suit.length; i++) {
for(int j=0; j<number.length; j++) {
card[num++] = suit[i] + number[j]; //看此处
}
}
一开始程序运行的不对,后来发现是在数组定义时出了问题,我将所有的’,’敲成了’+’,结果number数组就只有一个值……以后要注意这种细小的问题……
今天还接触到了Java中关于数组的类Arrays(需要导入import java.util.Arrays),可以用它来打印一维数组,如:
int[] a = {1,2,3};
System.out.println(Arrays.toString(a));
结果是:
[1, 2, 3]
打印二维或多维数组的话,只会当成一维数组打印,且只打印出地址,如:
int[][] a = {{1,2,3},{4,5,6}};
System.out.println(Arrays.toString(a));
结果是:
[[I@15db9742, [I@6d06d69c]
需要注意的一点是,Java的二维数组乃至多维数组里面的数组一定要套在大括号里,不能像C那样定义的二维数组却写成一维数组的形式(如int a[2][2] = {1,2,3,4})让电脑自己去划分,Java这样写会通不过编译的。
还有,要习惯用数组.length控制循环,如:
for(int i=0; i<suit.length; i++)
数组的初始化(静态方式)一共有两种:
1) int[] a = new int[] {1,2,3};
2) int[] b = {1,2,3}; 第二种方法写起来比较简单,建议采用第二种方法
遍历数组的方法for-each:
for-each可以用来遍历数组,但是无法访问下标值,也不能修改数组,例如:
int[] a = {1,2,3};
for(int k: a) {
System.out.println(k);
}
可变参数:这是个只能用于函数形参的参数,如传入函数的数组a,
public static void max(int[] a) { }
等价于
public static void max(int...a) { }
数组里有一点需要注意的是:二维数组里的一维数组长度可以不同,举个例子,
String[][] s = { {“张三”,”李四”},{“王二”,”秋香”} };
for(int i=0; i<s.length; i++) {
for(int j=0; j<s[i].length; j++) {
......
}
}
数组的length问题:
有的时候在遍历数组之前要做一个判断,例如:
之前定义了一个字符数组String[] strs
System.out.println(strs.length);
if(strs!=null) {
for(int i=0; i<strs.length; i++) {
......
}
}
为什么一定要做null判断呢?
我以前一直认为,数组如果是null的话,它的长度也应该是0,但其实不是这样的。我试了一下,以String数组举例可以分为三种情况:
- String[] strs = { } ; 我定了数组,但是不向里面放值,那么它的长度是0
- String[] strs = null; 或 String[] strs; 仅声明了数组,不能对它求length,会报错
- String[] strs = {“”} ; 存储空字符串也是有内容的,所以它的长度是1;同理,存两个空字符串的话,它的长度就是2
6.工具类(Arrays, Random)
Arrays:
第一步,导入Arrays类:import java.util.Arrays
第二步,调用相应的API:Arrays.方法名()
Arrays--> 1) toString(int[] args)
2) sort(int[] args)
3) sort(int[] args)
binarySearch(int[]args, int key) -->使用此函数需要先排序(即先调用sort)
Arrays里有个方法可以将String类型转换成char[]类型:
String str = “hello”;
char[] c = str.toCharArray();
Random:
Import java.util.Random;
nextInt(int num) --> 生成随机数0~num(不包含num)
调用时不能用Random.nextInt(int num)的方式调用,而是要创建一个Random的对象,由对象调用函数
7.赋值
1)对象之间的赋值
如果两个对象同属一个类,则可以进行对象的整体赋值。
A a1 = new A();
A a2 = new A();
a1.num = 10;
a1.name = "a1";
a2 = a1;
2)int类型以下的数运算后的赋值
int类型以下:byte,short 进行运算后,类型会升级到int,如:
byte b1=10;
byte b2=10;
byte b3=b1+b2; 此处会报错,应该改为:byte b3=(byte)(b1+b2);因为此时b1+b2的值的类型已经升级到了int,需要强制类型转换才能赋值给byte类型的b3
8.static(静态),final
static:
在类中,用static修饰的变量和方法都可以直接用类名调用。
static方法只能操作static变量
有static代码块,如
static {} 程序先执行该静态代码块中的内容。
注意:只有经过static修饰的成员变量可以放在static{ }中
final:
final定义的变量,不一定在初始化的时候指定值,可以在构造函数中指定值。但是必须给final变量指定值,否则报编译错误。
用final修饰的方法,表示该方法不能被重载,也不能被覆盖。
final和static可以同时修饰方法,顺序不唯一:
public static final void f1(){} (在接口中定义一个常量就是用这种方法)
或
public final static void f2(){}
总结:final定义的变量 --> 常量
方法 --> 不能被重载,也不能被子类重写
类 --> 不能被继承
9.修饰符protected, default(默认)
1)protected --> 由它修饰的方法可以被同一个包中的类通过”对象名.函数”的方式调用,也可以被它的子类调用。注意:子类不需要和它在同一个包中。
如:
BaseClass:
1 2 3 4 5 6 7 8 9 | package package_1; // 相当于NewObject类 public class BaseClass { // protected方法 protected void protectedMethod() { System.out.println("This is BaseClass"); }
} |
SubClass:
1 2 3 4 | package package_1;
public class SubClass extends BaseClass { } |
MainClass:
1 2 3 4 5 6 7 8 9 10 | package package_1;
public class MainClass { public static void main(String[] args) { BaseClass b1 = new BaseClass(); SubClass s1 = new SubClass(); b1.protectedMethod(); // 父类的protected方法可在同一个包中的其它类中被访问 s1.protectedMethod(); // 子类中继承了父类的protected方法 } } |
BaseClass和SubClass(BaseClass的子类)的对象都可以调用定义在BaseClass里的protected函数protectedMethod()。
缺省(默认):只有同一个包中的类能访问,子类在别的包也不能访问父类默认修饰符的变量或是函数。
10.方法的重载(overloaded)和重写
1)重载:
参数不同才是重载,如果只有方法的返回类型不同是无法构成重载的,因为计算机无法识别该调用哪一个方法。
11.值传递
Java只有值传递。这个和C、C++不一样,因为Java的设计是没有指针的。但是在学习的时候总觉得引用类型的参数传递很像是指针(引用传递),在网上查了一下发现不是这样的——不管用户传入的参数使基本类型还是引用类型,都是对原有值的复制。基本类型好理解,引用类型的复制指的是它的地址这个值被拷贝并传入函数,所以对这个地址做的任何操作都会反映到原先的对象上,就觉得像引用传递了。
public class test {
public static void main(String[] args) {
A a1 = new A();
a1.num = 1;
a1.name = "a1";
A a2 = a1; //a2和a1都指向a1
a2.change(a1); //改的是同一个存储空间的值
System.out.println(a2.name+" "+a2.num);
A a3 = new A();
a3 = a2; //如果两个变量都有自己的存储空间,就不会指向同一个位置了,只是单纯的整体赋值
System.out.println(a3.name+" "+a3.num);
a3.name = "a3";
System.out.println(a3.name+" "+a3.num);
}
}
class A {
public int num;
public String name;
public void change(A a) {
a.name = "change";
}
}
结果:change 1
change 1
a3 1
12.包名为何开头经常是com.
因为不想让自己定义的包名与其他人的包名重复,最好采用公司域名的倒写形式命名包,如苹果公司包名:package com.apple
注意,不需要写www
一般一个java项目需要定义三个packages:
dao --> 数据访问
service --> 业务逻辑
pojo --> 实体类
13.Eclipse软件的使用
1)Eclipse的编译选项在哪里
因为以前用的都是DEV的IDE,所以用Eclipse的时候有点奇怪为什么只有运行的选项而没有编译功能,以为将运行和编译两个功能合在一个运行的选项里了。今天在书上发现原来Eclipse的保存即是编译。
2)导入设置Java代码格式的文件
Window->Preferences->Java->Code Style->Formatter->import格式文件即可
14.向上转型与向下转型
向上转型在听翁凯老师课的时候好好地研究过,也就是用父类引用指向子类对象,如:Father son = new Son();
son只能调用Son类中重载Father类中的属性和方法。这里注意一点:重载的方法的修饰符一定要>=父类中的修饰符,如父类中定义函数:public void say(){},子类中重载就要定义成public void say(){},而不能定义成private void say(){}
son如果想要调用父类中没有,子类特有的方法时会报错,此时要解决这个方法就可以用向下转型的方法,即:用一个子类的引用指向父类的引用:
Son son2 = (Son) son;
注意,这里需要对父类引用进行强制转型,变成子类的引用。
动态绑定:向上转型
静态绑定:final,static,private 对于静态绑定的变量、函数,即使向上转型,父类引用调用的静态变量、函数还是父类的,而不是子类的。
如:
public class Father {
String name = "father";
public static void say() {
System.out.println("father say()");
}
public void say2() {
System.out.println("father say2()");
}
public static void main(String[] args) {
Father son = new Son();
System.out.println(son.name);
son.say();
son.say2();
}
}
class Son extends Father {
String name = "son";
public static void say() { //子类的重载函数也要修饰成static,否则报错
System.out.println("son say()");
}
public void say2() {
System.out.println("son say2()");
}
}
注意,这个程序在运行的时候Eclipse会让选择执行哪一个class,在这里选Father类,因为里面有需要运行的主函数。推测会出现这个选项的原因是两个class都有static函数导致程序不知道哪一个是真正的入口。
15.接口
接口中的方法默认都是public、abstract类型(可省略)
成员变量是public、static、final类型(可省略)
不可以用其他的修饰符(如private,protected),否则报错。
接口都是在类外新定义的,不被类所包含。
接口不能new一个对象,但是可以作为对象的引用,由接口的实现类new对象,如:
接口 对象名 = new 接口的实现类();
或者 接口 对象名 = new 接口(){(匿名内部类)}
抽象类 | 接口 |
|
√ | × | 构造方法 |
× | × | 实例化 |
接口可以继承另两个接口,用extends继承
16.内部类
内部类一共分为4种:成员内部类、局部内部类、静态内部类、匿名内部类。
1)成员内部类
成员内部类是作为外部类的一个组成元素,定义在外部类里。
成员内部类可以用各种修饰符修饰,即使是private;外部类也可以调用成员内部类的任意元素(即便是private);
可以直接使用外部类的所有元素,即使是private修饰的;
外部类和内部类是两个不同的类,在内部类中可以定义与外部类名字一样的成员变量/方法名;
但是成员内部类不能有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的内部类,这一点体现在它的调用上:
外部类 对象 = new 外部类();
外部类.内部类 对象2 = 对象.new 内部类();
Outer out = new Outer();
Outer.Inner in = out.new Inner();
示例代码如下:
public class Outer {
private int x = 5;
public static void main(String[] args) {
Outer out = new Outer();
out.out();
Outer.Inner in = out.new Inner();
in.inner();
}
public void out() {
System.out.println("This is out class!");
}
public class Inner {
public int x = 10; //注意此处对x的修饰符可以是public,因为需要外部类对象调用
public void inner() {
System.out.println("This is inner class!\nx="+x);
}
}
}
2)局部内部类
局部内部类定义在方法中。
局部内部类方法中要想用所在方法中的局部变量,必须将该变量声明为final类型,而且可以使用外部类中的非final类型。(新版本的jdk已经取消该限定)
可以和成员内部类一样,使用外部类的元素,即使是private类型。
只能在对应的方法中生成局部内部类的对象,生成对象方法和普通的一样,new即可。
不能被继承也不能实现接口,这个类只能在方法中调用一次,在方法外不能使用
注意:不能再方法体中定义方法,但是可以定义类
示例代码:
public class Outer {
public static void main(String[] args) {
Outer circle = new Outer();
circle.test();
}
private int r =6;
public void test() {
final double PI = 3.14;
class Circle implements T {
public double area() {
return PI*r*r;
}
}
Circle c = new Circle();
System.out.println("area="+c.area());
}
}
interface T {
public double area();
}
3)静态内部类
静态内部类和成员内部类差不多,有一点不一样:静态内部类被修饰成public static class...,可以有static属性和方法。
只能访问外部类中静态的成员变量。
实例化也没有成员内部类那么复杂:外部类.内部类 对象 = new 外部类.内部类();
示例代码:
public class Outer {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.check();
}
public static class Inner implements T {
public boolean check() {
System.out.println("check...");
return false;
}
}
}
interface T {
public boolean check();
}
4)匿名内部类
感觉原先听翁凯老师课的时候就觉得匿名内部类比较灵活,用的几率也大一些。匿名类就是在需要的地方直接new一个类。
匿名类可以当做方法的参数传递,也可以作为方法的返回值;
匿名内部类只能访问外部类的静态(static)元素;
这一句话是从书上看到的,目前不知道该如何应用,姑且抄下来留待以后应用:
匿名内部类中的this指的是匿名内部类本身,如果使用外部类中的this,则“外部类.this”;
注意匿名内部类使用接口时,implements语句的使用。
示例代码1:
public class Outer {
public static int a = 3;
public static void main(String[] args) { //注意此处并没有implements T,加上反而会报错,让把Outer
T t = new T() { //修饰成abstract类型,因为在Outer里并没有实现接口的函数,
public void t() { //而是在匿名类里实现的函数
System.out.println("t...");
a++;
}
};
t.t();
}
}
interface T {
public void t();
}
示例代码2(匿名内部类作为参数传递):
public class Outer {
public static void main(String[] args) {
Outer out = new Outer();
out.say(new T() {
public void t() {
System.out.println("ttt");
}
});
}
public void say(T t) {
t.t();
}
}
interface T {
public void t();
}
示例代码3(匿名内部类作为方法的返回值):
public class Outer {
public static void main(String[] args) {
Outer out = new Outer();
out.get().t(); //注意此处out.get()是一个T对象,所以可以调用t()函数
}
public T get() {
return new T() {
public void t() {
System.out.println("return T!");
}
};
}
}
interface T {
public void t();
}
17.包装类
1)String包装类
String --> int:
String numStr = “123”;//注意字符串形式上必须是整形数字才可以转换,否则会报错
Int num = Integer.parseInt(numStr); //因为parseInt()是static类型,所以可以直接用类名调用
String类的本质是字符数组char[],这一点和C是一样的。
一、对比生成字符串的两种方式
有两种方式生成一个字符串:
- String str = “abc”;
- String str = new String(“abc”);
不推荐第二种方法,因为java有一个String池,如果用第一种方法会现在池里找相对应的字符串,没有才会创建一个字符串,然而用第二种方法则是不在字符串常量池中查找,直接创建一个String对象,里面存储了一个地址,由此地址指向字符串常量池中的字符串。
例子如下:
public static void main(String[] args) {
String a = "Hello";
String b = "Hello";
String c = new String("Hello");
String d = new String("Hello");
System.out.println("a==b is "+(a==b));
System.out.println("b==c is "+(b==c));
System.out.println("c==d is "+(c==d));
}
结果是:
a==b is true
b==c is false
c==d is false
一开始遇到了问题,三个都是false,后来才发现其实是自己没有加括号,如:System.out.println("a==b is "+a==b);
这样就成了判断 ”a==b is +a”和 “b”两个字符串是否是同一个字符串,所以一定是false,但是加上括号就会先判断a和b两个字符串是否相等,结果就是true了。
在这里提一下trim()这个函数,可以去掉字符串两边的空格。
二、字符串截取为字符数组(split方法)
介绍一种将字符串截取成字符数组的方法,toCharArray()是一种方法,在这里介绍根据特定符号截取的方法split() :
String a = "a,b,c,d,e";
String[] chs = a.split(",");
for(int i=0; i<chs.length; i++) {
System.out.print(chs[i]);
}
结果是:
abcde
三、高效率低消耗的StringBuffer
为了减少对字符串进行修改的时候会新创建对象导致性能的消耗,同时StringBuffer的效率比较高,可以采用StringBuffer的方法进行字符串的追加(append)和删除(delete)。注意使用public StringBuffer delete(int start ,int end)函数,不会删除end位置的字符,而是会删除end的前一个字符。
例子:
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
System.out.println(buf.capacity());
buf.append("李敖先生").append("经常说:").append("他的前妻胡茵梦是相当的漂亮");
System.out.println(buf);
System.out.println(buf.capacity());
buf.delete(2, 4);
buf.insert(0, "大师");
System.out.println(buf);
}
结果:
16
李敖先生经常说:他的前妻胡茵梦是相当的漂亮
34
大师李敖经常说:他的前妻胡茵梦是相当的漂亮
四、String为null的小研究
之前有个印象,如果把null赋值给String类型的变量会出错,会报出NonePointerException的错误。但是重新尝试了一下,发现把null赋值给String变量,会打印出”null”字符串。
String str = null;
System.out.println(str);
而且如果给str添加新的字符串在打印时会加上前面的”null”:
String str = null;
str += "Abc";
System.out.println(str);
结果:nullAbc
但是如果用length函数去探测str的长度会报错NonePointerException:
String str = null;
System.out.println(str.length());
可是如果在str添加字符串以后再探测str的长度就不会报错,而且长度里算上了”null”的长度,为7
String str = null;
str += "Abc";
System.out.println(str);
System.out.println(str.length());
可是如果用下面这种方法:
String str = "";
System.out.println(str.length());
得到的长度是0,说明这样子创建了一个内容为空的字符串,而不是指向为空。应该用这种方法创建字符串:
String str = "";
str += "Abc";
System.out.println(str);
2)装箱与拆箱
JDK1.5以后有的功能:
装箱:基本数据类型 --> 包装类类型
Integer i = 10; <==> Integer i = new Integer(i);
拆箱:包装类类型 --> 基本数据类型
int j = new Integer(10); <==> Integer i = new Integer(10); int j = i.intValue();
3)next()和nextLine()函数的区别
next()是获取输入的一行字符串中第一个完整标记(我个人的理解是去掉空格后的第一个字符串,空格在字符串前或后都可以)。
例如输入字符串“ ,i like ”,用next()得到的字符串就是“,i”
nextLine()是获取一行内所有输入的字符,包括空格。
例如输入字符串“ ,i like ”,用nextLine()得到的字符就是它本身“ ,i like ”
18.异常
以前总是觉得throws和try-catch是一样的东西,但是其实二者有很大区别。
先简单介绍一下异常类:
首先是Java异常类的结构图
Exception中分为运行时异常(RuntimeException)和非运行时异常。运行时异常可以不处理,但是非运行时异常一定要进行捕获或抛弃处理。
异常处理的方法两种try-catch(-finally)和throws。
1)try-catch(-finally)
try-catch是比较常用的语句,在try中捕获可能出现的错误,然后交到catch中进行处理。
try语句可以嵌套try语句。
try块中可以抛出异常:
try{
throw new Exception();
}catch(Exception e){
System.out.println("ok");
}
可以有多个catch,但是排列顺序是先写范围小的Exception再写范围大的,如:
catch(ArithmeticException e) {
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
}
如果先写范围大的再写范围小的出现错误,表示小范围的那个Exception无法被执行。
finally这个语句不是必须要写的,它代表的是无论try里的代码是否出现异常都会执行finally里的代码。
try{
int x = 5;
int y = x/1;
System.out.println(y);
}catch(ArithmeticException e) {
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("Over!");
}
结果是:
5
Over!
注意:也可以不写catch,编译能通过,但是并没有处理这个异常。
finally之后的语句也会执行。
try{
int a=5/0;
}
catch(Exception e) {
System.out.println("e happens");
}
finally
{
System.out.println("finally");
}
int b = 5+6
System.out.println("end code" + b);
return 5;
结果是:
e happens
finally
end code11
5
而且没有finally,只有try-catch的时候后面的代码也会执行。
2)throws
它是用在方法声明后面的,而且可以同时指明多个异常,异常的出现顺序无要求:
public static void main(String[] args) throws Exception, ArithmeticException {
int x = 5;
int j = x/0;
System.out.println(j);
}
3)throw
throw用在方法体中,属于手动抛弃异常的方式。一开始我没明白什么是手动抛弃,后来知道了编程人员自己规定出现某个事件后,即便这个事件没有语义上的异常,但是编程人员将其可以设定为异常,并且用throw抛出该异常。
一般用法是throw new 一个Exception
如果想对throw进行异常处理,那么需要在方法的声明部分使用throws声明要抛出的异常。
注意一下,throws和throw都只是抛出异常,也就是一层层向外抛异常,有可能到达最外面的JVM进行处理,而它们两个不负责处理异常。
找到一个网页对三者进行了对比,可供参考:
http://blog.sina.com.cn/s/blog_62148d1e0100hkqc.html
4)常见的异常类总结如下:
1.非受检(运行时异常 unchecked exceptions):继承自Runtime的异常,不加try-catch也不会报错;程序员可以控制的异常
NullPointerException,空指针异常,你操作了一个空对象的属性或方法。String str = null; str.length();
ClassCastException,类型转换异常。Animal a = new Dog(); Cat c = (Cat)a;
ArrayIndexsOutOfBoundsException,数组下标越界异常。int[] arr = new int[3]; arr[3];
ArithmeticException,算术异常。 10/0
2. 受检(非运行时异常/编译异常 checked exceptions):继承自Exception的异常,必须添加try-catch,否则会报错;受外部环境影响,程序员难以控制发生的异常
FileNotFoundException, 文件未找到异常。
IOException, IO异常。
SQLException, 数据库异常。
SocketException,网络异常。
3. Error错误
这个问题与程序员没关系,指的是代码运行时,JVM(Java虚拟机)出现的问题。它和Exception不同,Exception是程序本身就可以处理的异常。
19.jar包的导入
书上的一种导入方式,导入的是log4j的两个jar包:
首先自己创建的Java Project上右键,新建一个Folder名为lib
20.JDBC
1)导入连接数据库的文件
首先要导入数据库相应的连接文件。我用的是mariadb,上官网下载了最新的连接文件
注意:要下载.jar文件。
按照19的方式把jar文件放入lib文件夹中,然后buildpath生成小奶瓶图标的文件即可
2)写连接的代码(反射的方式)
20.Java的安装方法
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
在这个网站下载合适的jdk,然后不加选择,每次往下一步直到完成,最后配置环境变量
环境变量设置:
在计算机上右键->属性->高级系统设置->环境变量
方法一:JAVA_HOME
新建一个环境变量JAVA_HOME,对两个路径进行设置:
C:\Program Files\Java\jdk1.8.0_121
然后在Path中用%进行引用
%JAVA_HOME%/bin;%JAVA_HOME%/jre/bin;
p.s.如果设置了classpath也需要进行引用,但是好像就不用设置JAVA_HOME了
方法二:Path(最简单的方法)
直接添加路径:
C:\Program Files\Java\jdk1.8.0_121\bin;
调试jdk是否安装成功:
dos界面敲入 java -version
如果成功,会显示Java的版本号
21.用dos运行Java
用dos运行程序:
cd(空格)文件所在的路径(可以直接拖拽文件,就得到文件的路径,注意要删去文件的名字及前面的斜杠,得到文件所在的目录);如果想要切换盘符,则直接输入 盘符:(回车) 即可 ,如: d:
javac hello.java -->得到编译文件.class(生成的.class文件和.java文件位于同一目录下)
java hello -->运行文件,得到结果
java的特点,一次编译,多次运行。只要编译成.class文件,该文件可以在任何java平台使用,不用管硬件与操作系统的差别。
出现的问题:编译文件成功,但是运行文件时出现问题:找不到或无法加载主类xx
解决方法:更改环境变量classpath,路径是当前文件所在的位置
22.多行注释的快捷方式
可以通过/*然后按回车出现:
/*
* 光标会停留在中间的*的位置上,按回车就会另起一行,行前面带有*标志
*/
23.运算符
一、>>>和>>的区别
首先有以下三种右移(或左移)的运算符:>>> , >> , <<
注意,没有<<<运算符
这三种运算符都是对二进制位进行移动,但是区别在于:
1)>>和<<是有符号的移动,一般移动后补零,但是如果是负数的话,向右移动最高位会补1
2)>>>是无符号的移动,不管数值的正负,都会在高位补0
这里有一个可以打印二进制形式数值的方法:
System.out.println(Integer.toBinaryString(3>>1));
二、&和&&的区别
&两边的操作数可以是数字,此时它返回一个数字;也可以两边是布尔值,此时它返回一个布尔值。两边类型必须同时为数字或同时为布尔值,不可以一个是数字,一个是布尔值。
如:int a = 13&12;
boolean b = true & false;
a的值为12,因为此时按位与。13的二进制是1101,12的二进制是1100,1101&1100=1100
&&的两边只能是布尔类型的值,如果两边是单纯的数字会编译报错。
三、“+”超过范围数字的结果
Integer是32字节,如果一个数本身超过32字节能表达的数的话,它会直接截取后32位作为新的数字(编译不会报错),如:int a = 1024*1024*1024*4;
它的二进制表示是1000……00,共33位,截取后得到的是000……00,所以a=0
同理int a = 1024*1024*1024*4+1; → a = 1;
24.面试题
1)用for写一个最简单的死循环
for( ; ; ; ) { }
2)使用final关键字修饰一个变量时,是引用不能变,还是引用对象的内容不能变?
引用不能变,但是引用的对象的内容可以变
3)Math.round(11.5) = 12; Math.round(-11.5) = -11; Math.round(-11.6) = -12; Math.round(-11.4) = -11 四舍五入的原理是在参数上加0.5然后进行下取整
4)下面的代码有什么不妥之处?
1. if(username.equals(“zxx”){}
答案:如果username是null会出错(NullPointerException),应该把可能为null的放在equals里:if(“zxx”.equals(username)){ }
2. int x = 1;
return x==1?true:false;
这个问题是x==1本身就会返回一个true或false的值,完全没必要在后面写true:false
也就是说三元表达式后面放true:false是一件很多余的事情,它应该用来赋值或做一些更有意义的事情。这道题直接写成int x = 1; return x==1; 更好
5)接口可以继承接口;抽象类可以实现接口;抽象类能继承具体类;抽象类可以有静态的main方法
6)写clone()方法时,通常都有一行代码,是什么?
是super.clone();
Clone 有缺省行为,super.clone();他负责产生正确大小的空间,并逐位复制。
7)abstract的method可以同时是static,native和synchronized
这里说明一下native,这个指的是调用了非java的接口,比如调用了C语言写的接口
用native声明的时候就像是声明一个抽象方法一样,是没有函数体的。
8)在运行时,由java解释器自动引入,而不用import语句导入的包是?
java.lang包括了一些基本类型,如Integer,String等等
25.常用快捷键
首先,如何设置快捷键?
Window->Preferences->General->Keys 在下方面板的Binding输入框按下相应的快捷键即可设置
1)想要复制一个类(可以用来进行调试):
单击选中的类,然后ctrl+C;
选中想要放入该复制类的包,ctrl+V;
重新命名后即可。
2)快速生成对象
想要新建一个对象,比如类名为Animal,新建一个Animal对象new Animal();后加上快捷键ctrl+1(此为数字1),补全该语句为Animal animal = new Animal();
3)将整行代码向上或向下移动
选中行,向上移动:Ctrl+Alt+”↑”
向下移动:Ctrl+Alt+”↓”
4)自动生成构造函数和get(),set()函数等等
Alt+shift+s
5)ctrl+鼠标单击函数名,会跳转到定义函数的地方
6)选中代码,右键 --> 选择Refactor --> Extract Method将相应的代码封装在一个函数里;或者使用快捷键Alt+shift+M
7)选中左侧列表文件,Alt+Shift+R给java的文件重命名
8)选中相应代码行,Alt+Shift+Z向下一选就是try-catch
9)一般控制台上的字符是不会被鼠标选中的,但是鼠标在控制台上右键选择“标记”就可以选中控制台的文本
10)全局搜索某个字符串ctrl+H->选择File Search 在Containing text文本框中输入要查找的字符串->点击“search”即可查找
11)快速导包/删除多余包ctrl+shift+O
12)可以设置:Next Tab->ctrl+Q
Previous Tab->ctrl+Tab
13)回退上一步Alt+←
进入下一步Alt+→
14)ctrl+shift+R
搜索资源,可以输入文件名查找文件
15)光标在待查询的地方,然后ctrl+shift+G
可以搜索得到所有调用它的地方。点击右上方的加号可以把所有查询的位置展开
26.基础内容的补充知识
1)双层循环嵌套的前提下,想用break从内层循环直接跳出最外一层的循环,可以给循环定义一个别名,例如:
a:for(int i=0; i<5; i++) {
b:for(int j=0; j<i; j++) {
System.out.println(“hello”);
break a; //直接由此处跳出最外层循环
}
}
2)switch-case可以用String和char作为case中的常量,如:
......
case ‘a’:......
或是
case “hello”:......
此功能是JDK1.7版本以上才能有的,万幸的是现在几乎所有公司用的都是JDK1.以上版本
switch的作用范围有:byte,short,int,char,String,enum (long不可以)
3)char类型的默认值是空格
4)创建对象的时候,没有对对象的成员变量初始化,则成员变量都是默认值(数组也是一样的),JVM帮助赋值的
5)this(参数列表)调用相应的构造函数。比如构造函数为:Animal(String name,int age),调用时可以是this(“tiger”,5);
6)如果调用main方法,可以写成main(args);
7)Java有叫做保留字的东西,即不排除以后会启用,变成关键字的情况。但是现在是不能使用的。Java的保留字有:byValue, cast, future, generic, inner, operator, outer, rest, var , goto ,const
27.堆内存与栈内存
栈内存:针对的是基本数据类型,当定义的变量不再使用时,会进行垃圾的自动回收
堆内存:针对的是引用类型,比如说数组int[]a,数组的所有值会存在一个堆内存中,然后将堆内存的首个地址存放到栈内存中,如图所示
28.static{ } ,{ } ,构造方法 的执行顺序
执行顺序(从高到低):
静态语句块static{...} > 非静态语句块{...} > 构造方法
其中,静态语句块仅能执行一次,非静态语句块取决于创建多少次对象(创建多少次对象就执行多少次):
比如我在一个类里定义了一个static语句块,我创建多个类对象只会执行一次static语句
public class A {
static{
System.out.println(“hello”);
}
}
A a1 = new A();
A a2 = new A();
A a3 = new A();
结果:输出一次hello
但是如果改成普通的代码块(删除static),则会输出三次hello
加入继承的情况:父类static>子类static>父类{}>父类构造方法>子类{}>子类构造方法
例如:
public class Test extends A {
Test() {
System.out.println("Test constructor");
}
static {
System.out.println("Test static");
}
{
System.out.println("Test code");
}
public static void main(String[] args) {
Test t1 = new Test();
Test t2 = new Test();
}
}
class A {
A() {
System.out.println("A constructor");
}
static {
System.out.println("A static");
}
{
System.out.println("A code");
}
}
结果:
A static
Test static
A code
A constructor
Test code
Test constructor
A code
A constructor
Test code
Test constructor
29.策略设计模式
将接口作为类的一个属性,然后在方法作为参数中调用它,当生成一个子类对象进行向上构造的时候,会调用相应的接口方法——延迟实现接口的方法。
例子(代码节选):
Person类:
private String name;
private Isay isay;
public Isay getIsay() {
return isay;
}
public void setIsay(Isay isay) {
this.isay = isay;
}
public static void main(String[] args) {
Person p = new Person();
p.setIsay(new Employee());
p.getIsay().Isay("基层人员");
Person p1 = new Person();
p1.setIsay(new Middle());
p1.getIsay().Isay("中层干部");
}
接口Isay:
public interface Isay {
public abstract void Isay(String name);
}
员工类1:
public class Employee implements Isay {
@Override
public void Isay(String name) {
System.out.println(name+"给我活儿干");
}
}
员工类2:
public class Middle implements Isay {
@Override
public void Isay(String name) {
System.out.println(name+"我要干活儿");
}
}
30.导入jdk中存在但默认不允许使用的类文件
举个例子,比如用来进行MD5编写的类Base64Decode和Base64Encode存在于sun.misc包中,1.8某版本以下默认是不允许使用这两个类的,所以要进行设置,让他们的属性可以变得可以使用。
可以在对应的项目或是类文件右键 --> Build Path --> Configure Build Path... --> Libraries
双击第一条Access rules
选择Add,按下图进行配置,ok即可
31.配置源码的方法
下载一个src.zip(不一定叫这个名字,但是就是jar文件的压缩包)。
比如想要查看Exeption的源码时,没有配置的时候它会提示需要Attach Source,点击后选择External File ,找到src.zip的路径并添加即可
我个人觉得,如果想要换一个源码压缩包可以在相应的文件上右键 --> Build Path --> Configure Build Path... --> Source处进行修改
32.clone()
这个方法是我在书上看见的,主要的作用就是复制一个对象。
要使用这个函数就必须实现Cloneable接口,并且重写clone()方法。
使用clone的时候不多,很多时候会在游戏中用到,因为要使用很多一样的对象时,new很多一样的对象会耗费很多内存,这个时候用clone就很好,不用新建对象,减少了创建对象的内存空间。
目前还不太会用,以后用到的时候再附上代码吧。
33.Random和Math.random()
首先,这两个都能产生随机数,但是第一个是类,第二个是Math中的一个方法。
举个例子:
想要产生一个97-122的随机整数,分别用两种方法来写:
1)Random
Random random = new Random();
int n = random.nextInt(26)+97;
2)Math.random()
double random = Math.random();
int n = (int)(random*26+97);
Java中产生的随机数据网上的讲解来看都是伪随机数,也就是通过一定复杂算法得到的数字,可以预知的数字。详情介绍可以参考该网址:http://www.cnblogs.com/greatfish/p/5845924.html
34.线程
1)wait()和notify()
这两个分别是等待和唤醒线程。这两个方法需要配合使用,并且要放在同步函数synchronized里。通过对API的查询我发现如果有多个线程在同一个对象上等待,如果使用notify()唤醒,则会随机选择其中一个线程唤醒(官方说法:唤醒此对象等待池中的一个线程);如果使用notifyAll()方式唤醒,则会将在此对象上所有等待的线程都唤醒(官方说法:唤醒此对象等待池中的所有线程)。
经过对两个题目的总结,我认为如果想要用这两个函数实现线程的切换,那么类的格式如下:
首先,要定义一个用来进行切换的boolean变量。
然后将要进行切换的两个自定义函数放在同一个类里。这是一个普通类,不实现Runnable接口。但是这两个函数都必须用synchronized加以修饰。
示例代码如下:
class ChangeThread {
private boolean sub = true;
public synchronized void sub(int i) {...}
public synchronized void main(int i) {...}
}
每个函数的格式大体如下:
- 判断语句(使用boolean变量检测是否使其等待)
- 未进入等待状态,进行相应操作
- 将boolean变量值反转
- 唤醒在此对象上等待的线程
示例代码如下:
public synchronized void sub(int i) {
while(!sub) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int j=0; j<10; j++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sub: No."+j+"of loop"+i);
}
sub = false;
this.notify(); //这里应该是唤醒在ChangeThread对象上等待的线程
}
注意一下,boolean的值在判断的时候,两个函数正好是要起到相反的验证效果。
主体代码是两个线程,给出的一个示例代码中以main作为其中一个线程的代码,其实也可以自己定义两个类实现Runnable接口,然后得到两个线程分别start()即可。
两个示例的完整代码放在github:JavaBasic\Thread里。
2)单线程池与多线程池
首先要知道Executors是java.util里的一个类,这里面有两个方法:
newSingleThreadExecutor ==>创建一个单线程池
newFixedThreadPool(nThreads) ==>创建一个多线程池
单线程池:
单线程池的功能在于,如果我们创建了一个Runnable的类,然后我要创建多个线程都调用这个类,那么每次new线程再销毁会很麻烦。所以可以创建一个单线程池,我可以多次运行该线程,而且该线程在执行结束后不会被中断,而是等待下一次调用——这样就不用new一个新的线程了。
注意,单线程池中只有一个线程。
示例代码:
Thread th=new Thread(mr); 其实使用这个方法就相当于使用了
th.start(); 蓝色的这两行代码
ExecutorService es =Executors.newSingleThreadExecutor();
es.execute(mr);
es.execute(mr);
es.execute(mr);
注意:execute()里的参数是一个实现了Runnable接口的类,而不是Thread线程
多线程池:
可以使用不同的Runnable;可以创建多个线程。注意,如果创建的线程数超出定义线程池的最大数量,那么会将多出来的线程放在缓冲区,等到线程池中有线程空闲的时候再去帮忙执行这个线程的操作。
详情代码见github : JavaBasic\Thread\ThreadPool.java
3)计时器Timer
有一个可以控制操作开始发生的延迟时间和操作间隔时间的方法。它可以重复使用该方法,直到达到某一条件调用cancel()终止执行为止。
示例代码:
static int i = 0;
public static void main(String[] args) {
Timer tm = new Timer();
tm.schedule(new TimerTask() {
@Override
public void run() {
if(i<10) {
System.out.println("我执行"+i+"次啦");
i++;
}else{
tm.cancel();
}
}
}, 500, 1000); //第二个参数是最开始执行操作的延迟时间(在本例中是500ms),仅使用一次;第三个参数是操作之间的间隔时间(在本例中是1s)
}
完整代码见github : JavaBasic\Thread\TimerTest.java
4)同步synchronized
运用synchronized实现同步可以有两种方式:
synchronized(对象){ } -->包裹 一般来说,对象就是this
用synchronized修饰方法
注意,第二种方法需要考虑修饰的函数是否是静态函数,如果是静态函数,即便用synchronized修饰,也可能会出现不同步的情况。因为synchronized锁住的是对象,而静态函数是属于整个类的,所以应该用synchronized代码块锁住类这个对象才能实现同步操作。
如:
public class Test {
public static void function() {
synchronized(Test.class) {......}
}
}
完整代码见github : JavaBasic\Thread\SynchronizedTest.java
5)run方法和start方法的区别
start方法:
用这个方法只是启动了一个线程,只是让这个线程处于就绪状态,并没有运行。该线程需要得到cpu时间片的时候才开始运行run()方法。
run方法:
当前线程使用run()方法,只是普通方法,并没有实现多线程运行。
35.github的使用方法
删除仓库(repository)
在相应仓库的上方选择Settings,然后拉到最下方,有一个delete repository,选择并删除即可。
36.遍历的方式(for-each & Iterator)
简单的遍历如for,while就不多说了,在这里可以看一下for-each和Iterator的遍历,这两个在进行数据打印时起到便捷的作用。
其实这两个方法我认为最大的不同是for-each不能对数据进行修改,基本上就是用来输出的;而Iterator则不一样,它是用next()直接取对象,不仅可以用来输出,还可以对数据进行修改。
在这里多说一点关于Iterator的知识:
1)List和Set都可以使用迭代器Iterator,因为List和Set的超级接口使Collection,Collection的超级接口是Iterator,所以它们都是可以使用迭代器的
2)它有两个函数hasNext()和next()
hasNext() --> 判断当前游标的右侧是否还有对象,返回boolean值
next() --> 取当前游标右侧的第一个对象
3)注意,游标的位置是不能返回的,所以如果想要迭代器从头开始迭代就需要new一个新的迭代器对象。
Iterator<Employee> it = em.iterator();
it.next().setId(5);
Iterator<Employee> iter = em.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
37.Comparator和Comparable的区别
我原先在这两个的区分上一直比较糊涂,这一次参考了网上的一些说法,也自己亲自试验了一下,总结如下:
我如果定义了一个类,作为以后要new的对象,那么我在测试类里想要根据我自己制定的某种规则对该对象进行排序。那么这里就有两种方法来解决这个问题:Collections.sort(List) & Collections.sort(List,Comparator的对象)
如果想使用第一个方式,让计算机自己调用我重写的比较函数,那么我需要让对象类实现Comparable接口,并重写compareTo函数。注意,这个函数传入的是一个对象。
例子:
对象类中重写该方法:
@Override
public int compareTo(Employee o) {
return this.id-o.getId();
}测试类中调用该方法:
Collections.sort(list);
如果我想比较两个对象,那么可以这样写(em1,em2是创建出来的两个Employee对象):
em1.compareTo(em2)
返回的是int类型的值,如果是负数,那么说明em1<em2(取决于对compareTo函数的重写)
接下来讲一下Comparator:
如果让类实现Comparator接口,意味着你要使用的排序函数就是第二种Collections.sort(List,Comparator的对象)
使用上面这个函数有两种写法:
1)Collections.sort(list, new Employee());在这里new的是对象类Employee的一个对象
2)Collections.sort(list, new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return (int)(o2.getSalary()-o1.getSalary());
}
});
在这里面是直接生成一个Comparator的对象
在对象类实现了Comparator接口的前提上,如果我们想要比较两个对象,那么可以这样写(返回的也是int值):
Employee employee = new Employee();
System.out.println(employee.compare(em1, em2));
我个人觉得Comparator更加灵活一些,原因有二:
一、如果我想按照不同的规则去排序对象的话,显然用Collections.sort(list, new Comparator<Employee>(){...});现生成一些排序规则才可以,比如我想对员工按照工资降序排列或按照id升序排列,这个时候光在对象类中重写一个方法是无法满足两种排序的要求的。
二、就算只按照一种规则去排序,那么使用Collections.sort(list, new Employee());方法也很简单,还能应对未来的扩展需求(比如需要新增加另一种方式排序)。
38.格式化输出
我一直有个疑问,如果想让double数据类型只输出到小数点后两位,不用c语言的printf怎么做?后来上网查了一下,发现可以用这种方式输出:
System.out.println(String.format(“%.2f”,153.33698));
输出的结果就是153.33
39.Date日期
1)date.getTime()得到的是一个long类型的数据,可以用来计算
我想要计算一个程序运行的时间就可以在程序的开头和结尾分别调用这个函数,进行相减即可。也可以用另一种方法,原理都是一样的:
System.currentTimeMillis()这也会得到一个long类型的数据,然后操作方法同上,相减得到运行的毫秒数
40.IOStream 输入输出流
1)关于使用new String(b, 0, len);构造方法的原因
在预习的时候就有点奇怪,为什么一定要用String s = new String(b, 0, len);这种方式构造一个字符串,觉得直接new String(b);不是也可以吗?结果上课发现不这样写是会出问题的,问题主要源于我的代码中在设定byte[]数组的大小时,设置成了1024。当文件中字少的时候发现不出来,一旦文件中的字的大小超过1024个字节就会发现输出有重复的现象。
举个例子:
public static void main(String[] args) throws IOException {
File file = new File("D:/test1/a.txt");
InputStream in = new FileInputStream(file);
byte[] b = new byte[8];
while(in.read(b)!=-1) {
String s = new String(b);
System.out.println(s);
}
}
这样输出结果就是:
而原txt文件是这么写的:
可以注意到在“?”后又出现了一次“好不好”。这是因为每次取8个字节的内容并输出,虽然在in.read(b)中只读到文本最后的部分,但是b数组之前的内容没有被覆盖,所以会输出上一次存储的文本。
应该改成:
File file = new File("D:/test1/a.txt");
InputStream in = new FileInputStream(file);
int len = -1;
// byte[] b = new byte[1024];
byte[] b = new byte[8];
StringBuilder sb = new StringBuilder();
while((len=in.read(b))!=-1) {
String s = new String(b, 0, len);
System.out.println(s);
}
结果就是:
2)输出流在文件不存在的前提下会自动创建文件
3)对象的序列化与反序列化
序列化和反序列化的意思是将一个对象转变成二进制的数据进行存取。
那么首先要对对象类添加点代码:
一、实现Serializable接口:
这个接口并不需要重写什么方法,它只是一个标识性接口,目的是让JVM知道这是一个序列化的对象,在存取时以二进制的方式进行存取。
public class Person implements Serializable {}
二、(可选)生成序列号:
这个并不是必选项。生成序列号的意义在于当java升级以后,它也会兼容以前的版本,让对象能够正常地序列化。建议最好是加上,并且选择第一种添加序列号的方式:
private static final long serialVersionUID = 1L;
对象序列化注意一点,如果写入文件的是一个对象数组或List等等,当取出文件的时候,必须也取出数组或List等等,即怎么存入的怎么取出,否则会出现异常ClassCastException
举个例子:
存入文件List<Person>
List<Person> ps = new ArrayList<>();
ps.add(person1);
ps.add(person2);
oos.writeObject(ps);
正确取出文件:
List<Person> ps = (List<Person>) obj.readObject();
错误取出文件:
Person p1 = (Person) obj.readObject();
Person p2 = (Person) obj.readObject();
对象类的序列化中还有个标识:transient,把它放在哪个属性前面哪个属性就不会被序列化,虽然对象中的属性存在,却不会将该属性的值存入文件,它只有默认值。
transient private String name;
比如我不让name一同被序列化,那么就算我将赋值后的name随着对象一起存入文件,取出来的也是一个null值。
全部代码可见github:JavaBasic/IOStreamDemo的Object相关的两个文件
41.File文件
1)相对文件路径的写法
如果在当前Project文件新建了一个文件,比如test.txt,我用File file = new File(“test.txt”);可以找到这个文件。因为程序运行的时候,当前Project的目录即为根目录,程序会在这个目录下查找指定文件。
所以,如果我把test.txt放在了Project下的src文件夹中,那么文件的相对路径就应该是:”src/test.txt”
如果我把test.txt放在了src下的streamWorks包中,那么相对路径就是:"src/streamWorks/test.txt"
注意:开头没有”/”。
2)file.delete() -->只能删除空文件夹或单个文件
42.编码与解码
给出一些简单的编码分类吧,太深奥的研究了也暂时用不上,还记不住。
GBK兼容gb2312,也就是说GBK的范围比gb2312是大的。这两个都是汉字编码格式。
Unicode可以编码全世界所有字符,分为UTF-8, UTF-16和UTF-32。
需要注意的是GBK和Unicode并不兼容,尽管它们都能进行中文字符的编码。
顺便说明上面一点,虽然都有汉字编码,但是给同一个汉字编的码不一样。当我用GBK对一个汉字进行编码,用UTF-8进行解码的话,UTF-8会去寻找GBK编码的值,读出来的中文字符就不一样,于是就产生了乱码。
从对输入输出流的操作上就可以看出来,其实String这个类是有编码解码操作的。因为我用String对象可以解码出一个二进制的数据:(前面的b是byte[]类型)
String s = new String(b, 0, len);
String的编码操作:
String str = new String();
str.getBytes(“Hello”); 将字符串转换为byte[]
而且String还能按照指定的编码方式进行编码:
String s1 = new String("hello".getBytes(), "utf-8");
String s1 = new String("hello".getBytes(), Charset.defaultCharset());
s1 = new String("你好".getBytes(),"ISO-8859-1");
System.out.println(s1);
注意最后一个会输出四个问号,因为ISO-8859-1并没有对汉字的编码,所以它在编码的时候只能安上四个问号的编码来代替,所以解码后就能只看到四个问号。
43.如何用URL下载文件
URL url = new URL("http://staticfile.tujia.com/upload/info/day_140829/201408291110208765.jpg");
// HttpURLConnection conn = (HttpURLConnection) url.openConnection();
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
OutputStream out = new FileOutputStream("pic.jpg");
int len = -1;
while((len=in.read())!=-1) {
out.write(len);
}
out.close();
in.close();
在这里我注意到一点,尽管老师上课说要使用HttpURLConnection生成一个连接对象,但是我发现用URLConnection同样可以完成相同的操作。上网查了一下二者的区别:
URLConnection和HttpURLConnection使用的都是java.net中的类,属于标准的java接口。
HttpURLConnection继承自URLConnection,差别在与HttpURLConnection仅仅针对Http连接。
至于仅针对http这个连接有什么用,可能得留到以后学习JavaWeb的时候才能知道了。
全部代码可见github/JavaBasic/URLDemo
44.重置mariadb的密码
我刚才一度非常郁闷,因为我想改一下我数据库的密码,结果按照命令行改着改着就出现了一个我无法解决的问题,怎么写也没用。后来网上一通查,按照mysql的方式以不检查权限的身份登入数据库然后修改密码,最后终于成功了……
分享一下链接:
http://www.cnblogs.com/mumue/p/3816185.html
45.在忘记了自己的jdbc连接数据库语句时,如何找回
在MyEclipse的右上方单击图标
选择MyDatabaseExplorer->跳转界面->在该界面new一个项目,可以看到下图中的Connection URL,这个就是JDBC的连接语句。(可以在Driver template里选择对应的数据库软件)
46.DBUtils
想要使用这个类先要导入jar包
这个jar包可以到commons开头的Apache官网下载。
注意使用这里面的方法时,必须保证实体类的属性和数据中的属性一模一样,例如:
数据库user表中有两个数据元素:
id int和name varchar(50)
则在实体类User中定义属性:
private int id;
private String name;
注意:User类中的属性不能有别的,也不能重命名。
47.Math类
1)Math.cos()&Math.sin()
这两个函数都是计算“弧度”的余弦值和正弦值。
例:计算42度的余弦值→double d = Math.cos(Math.toRadians(42));
48.继承
1)构造函数
Java中子类不会继承父类的构造函数,而是调用父类的构造函数。
一般来讲,创建一个子类对象时会先调用其父类的构造函数(默认是无参构造函数super()->可以不写,系统默认调用),然后再调用子类自身的构造函数。如果父类没有无参构造函数,则要在子类的构造函数中显式调用父类的有参构造函数super(...),否则会有编译异常。
例如:
public class Test {
Test(String s)
{
System.out.println("Test");
}
public static void main(String[] args) {
A a = new A("haha");
}
}
class A extends Test {
A(String s) {
super(s);
System.out.println("A");
}
}
49.判断两个对象是否相等
1)hashCode()与equals()的相互关系
一般来说,创建一个class会重写三个继承的方法:toString,equals和hashCode。前两个用的较为频繁,最后一个我之前有学过但是没怎么用过。hashCode应用频率较低的原因是它和equals都有判断两个对象是否相等的作用,但是在使用HashMap,HashSet,HashTable的时候,再判断对象是否相等这一点上比equals的效率高。
以HashMap为例,下图为HashMap的结构图:
在添加新对象的时候会先计算该对象在HashMap中存储位置(哈希值),如果该位置没有元素,则放入;如果该位置有元素则在该链表中迭代比较key,key相等则覆盖,key不等则插入到该链表的表头。
规律:hashCode相等的对象key可能不等,而key相等的对象hashCode一定相等
附:截取的put源码
//计算key的hash值
int hash = hash(key.hashCode());
//计算key hash 值在 table 数组中的位置
int i = indexFor(hash, table.length);
//从i出开始迭代 e,找到 key 保存的位置
for (Entry<K, V> e = table[i]; e != null; e = e.next) {
Object k;
//判断该条链上是否有hash值相同的(key相同)
//若存在相同,则直接覆盖value,返回旧value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value; //旧值 = 新值
e.value = value;
e.recordAccess(this);
return oldValue; //返回旧值
}
}
如果我从HashMap单纯用equals方法找到一个指定对象,那么需要和很多对象依次进行比较,效率很低,但是如果先用hashCode锁定一个区域再在这个区域中用equals找指定对象,进行比较的此处就大大降低,提高了效率。
重写hashCode还有一点就是避免如在HashSet中存入相同的对象。
如:
Person类:
public class Person {
private int age;
Person(){}
Person(int age) {
this.age = age;
}
@Override
public int hashCode() {
return age%10;
}
}
主方法:
public static void main(String[] args) {
Person p1 = new Person(10);
Person p2 = new Person(10);
HashSet<Person> people = new HashSet<Person>();
people.add(p1);
people.add(p2);
System.out.println(people);
}
输出结果:
[abc.Person@0, abc.Person@0]
由输出结果可知存入了两个相同对象,这样HashSet就失去意义了。所以在这种情况下,equals和hashCode两个函数都必须要重写。
2)==,equals,compareTo
再判断两个对象是否相等的时候==是不推荐的做法,所以往往会用equals做比较。但是equals在面对一些数字类型的对象做比较的时候也会有问题,例如:
public static void main(String[] args) {
BigDecimal a = new BigDecimal("100.0");
BigDecimal b = new BigDecimal("100.00");
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a.compareTo(b));
}
输出结果:
false
false
0
equals认为100.0和100.00是两个数,返回了false,与期待的结果可能就不一致了。所以这种情况用compareTo更为合适。