JavaSE学习过程中问题总结 —— 基本概念与常用类等

找工作失败发现了自己的问题之后,觉得还是基础比较差。所以重新开始,用将近半个月时间从SE部分把知识重新过了一遍,总结记录一下整个过程中有价值的问题和一些注意事项,面试应该也用得到:

有几个地方没有自动换行,自己在逗号的地方随意回车了,看的时候不知道有没有障碍。

这算是编辑器bug吧?

1、数据类型

基本数据类型8种:

数字型byte、short、int、long,默认为int型

浮点型float、double,默认为double

字符型char,布尔型boolean

引用类型:String等。

注意事项:

这里会经常被问到String是不是基本数据类型,由于使用方法平时与基本类型很相近,容易混淆

声明float类型直接赋值时,数字后面要记得加f,因为默认浮点数是double,需要进行转换。同理声明long时,数字后加L

例如:float f = 12.345f;    long l = 1000000000000000L;

自动转型时顺序:byte,short,char->int->long->float->double

向下转型时需要强制转换,但需要注意丢失精度问题

例:byte b1=3,b2=4,b3;

b3=b1+b2;//error,变量相加,类型提升为int,不能直接赋值给b3

b3=3+4;//这样就没问题,计算后在byte范围内

另外,byte范围是-128~127

如果强制赋值byte b = (byte) 130;根据补码截取计算,b=-126;

2、运算符以及各种输出

(1)加号+在数字之间为运算符,但如果先出现过字符串,则变成了连接符,这里经常会有输出的问题

例:

System.out.println(5 + 5 + "5");//这里输出为105

System.out.println("5" + 5 + 5);//这里输出则是555

第一行输出在碰到“5”之前,+还是运算符,直接计算5+5

而第二行从一开始+就成了连接符,每个元素直接按字符串拼接。

(2)结合前面赋值类型问题,这里还有一个需要注意的事

例:short s = 1;

s = s + 1;与s += 1;

跟上面一个例子一样,左边s+1被认定为int型,不能直接赋值给short型

而右边就没有问题,因为+=符号隐含了一个强制类型转换,相当于s = (short) (s+1);。

(3)^符号,同0异1,根据这个特性可以知道一个数据对另一个数据异或两次,结果不变

例子:实现两个变量交换

这里当然不是最简单的通过一个中间值tmp来实现交换,而是采用^符号

x = x^y;//

y = x^y;//这里y = x ^ y ^ y;异或两次返回x赋值给了y

x = x^y;//x = x ^ y ^ x;返回y赋值给了x实现交换

注:还可以通过运算符优先级来一句话实现 y = (x + y) - (x = y);

(4)位移运算符

<<:左移右边补0,如果计算值,可以看做乘以2的移动次幂

>>:右移为除以2的移动次幂

>>>:无符号右移,因为>>右移时最高位是什么就补什么,而这个就是补0

例:最高效率求2*8

2<<3;//相当于2*(2^3);

3、流程控制语句

顺序、选择、循环

这里就是编程语言都通用的部分,很基础的联系了一些简单算法

4、数组

初始化:

动态初始化 数组类型[] 数组名 = new 数组类型[长度];

静态初始化 数组类型[] 数组名= { , ,};

直接以数组名输出,结果是地址值。内存和地址问题很重要,后面具体还会总结。

数组常见异常:

ArrayIndexOutOfBoundsException,数组越界异常,访问了不存在的索引,在循环遍历中需要注意

NullPointerException,空指针异常,数组不指向堆内存

接着是一些数组遍历、取最值、逆序等方法,都很基础,并且后面有专门的工具类提供方法。

二维数组有个小问题,初始化格式中 int[] y[];这样也是二位数组。

5、面向对象思想

强调对象,把复杂的问题简单化,从执行者变为指挥者

不需要考虑过程,也不需要考虑每个对象具体怎样实现,只需要访问对象,让其完成对应功能即可

6、类

(1)包括三个部分:

成员变量、构造方法、成员方法

(2)在成员方法中的变量为局部变量,他与成员变量的区别是

类中位置不同:但名字可以相同,调用时就近原则

内存中位置不同,结合生命周期:成员变量随着堆中对象消失,局部变量随着栈中方法调用完毕而消失

(3)形参的传递问题:

基本类型不影响实参,引用类型直接影响实参(但String类型此时又与基本类型用法相似,不影响实参,后面还有例子)

(4)匿名对象:

直接new一个对象来调用他的方法,而不用引用指向他。

当这个方法只用一次,可以用匿名对象实现,调用完毕就变成垃圾等待回收,节省空间。

可以结合安卓中按钮监听对象的使用来理解。

7、封装

引入private关键字,将类中的成员变量隐藏起来,不能直接通过.号来访问数据

提供对应的get和set方法,隐藏对象的属性和实现细节

提高代码的安全性和复用性

(1)this关键字:

我们给出对外公共的set和get方法时,为了方便使用和程序易读,

传入参数名和成员变量名一般是一致的

这时就需要使用this关键字来调用类中的成员变量,格式:this.成员变量 = 参数名;

this代表了此类的对象,虽然对外界成员变量是私有的,

但是类中还是可以直接以.号调用,从而实现这样的赋值

(2)构造方法:

每当我们new一个对象时,调用的都是他的构造方法。

但最开始我们其实并没有去写,因为系统默认会给一个无参的构造方法

(这里的原因是Object类中,只默认一个无参构造,

又因为所有类默认继承与此类,所以继承时只有一个无参构造方法)

格式:方法名与类名相同,没有返回值类型和返回值。

当我们自己去给出了一个构造方法,不管有没有参数,

系统将不再提供默认的无参构造,所以开发中尽量都是自己将构造方法写好。

(3)重载:

提到构造方法就要提到这个概念,构造方法可以有多个,

除了默认的无参构造,还可以传递参数,类似直接将set与get方法整合

写多个构造方法的过程就是重载,也有一定的规则:

每个构造方法的参数列表要不同,这里要理解的是,不光是个数与类型不同

同样两种参数,顺序不同也算不同

(4)static关键字:

静态的,被所有对象共享,随着类的加载而加载,用于定义一些所有对象共有的属性或方法

可以直接通过类名进行调用,所以很多常见工具类的方法都是静态修饰的

注意事项:

static中没有this关键字,因为静态内容是随着类的加载而存在,this只有对象创建时才存在

即静态的内容是比对象先存在的

静态方法无法访问非静态变量或方法,但非静态方法可以访问静态变量与方法

(5)代码块:

用{}括起来的部分,为代码块。分为局部代码块、构造代码块和静态代码块

局部位置代码块一般用于限定变量的生命周期

构造代码块是在类中成员位置的代码块,每次调用构造方法前都会执行,

用于将多个构造方法中共同的部分写在同一个代码块中,对对象进行初始化使用

静态代码块直接以static修饰代码块,只执行一次,类初始化时执行。

注意事项:

代码块的执行顺序为 静态-》构造-》构造方法,其中静态只执行一次,构造在每次调用构造方法都会执行

例子:看程序写结果

public class Test3 {
	static {
		System.out.println("static");
	}
	public static void main (String[] args) {
		System.out.println("main");
		Student s1 = new Student();
		Student s2 = new Student();
	}

}

class Student {
	static {
		System.out.println("静态代码块");
	}
	{
		System.out.println("构造代码块");
	}
	public Student () {
		System.out.println("构造方法");
	}
	
}

这个程序输出为

static

main

静态代码块

构造代码块

构造方法

构造代码块

构造方法

分析:首先进入带有main方法的测试类,其中有一个静态代码块,需要先执行,输出static

然后进入main方法,顺序执行,先输出了main

接着才是创建引用,此时才回初始化学生类,初始化过程中有一个静态代码块,直接做了输出

再指向new的对象,此时对象才被创建,调用构造方法,并先执行构造代码块

当再次引用另一个变量来指向新的对象时,类在刚才已经被初始化,不再执行静态代码块,

直接输出构造代码块和构造方法中内容

8、继承

(1)定义:开发中经常遇到许多类会有共同的地方,例如猫狗,都是动物,此时就需要使用继承

定义一个父类,具有所有子类共有的属性和方法,用extends关键字实现继承

提高代码的复用性和维护性,使类之间产生了关系,是多态的前提

但也有弊端,开发的原则是低耦合高内聚,继承是一种类之间耦合性的增强

注意事项:

Java中只有单继承,不能多继承,可以多层继承

(但是可以通过接口的方式间接实现多继承)

继承过程中,子类只能继承非私有的成员

子类不能继承父类的构造方法,但可以使用super关键字访问父类构造方法

(2)构造方法细节:

子类可能会访问父类的属性,所以当子类初始化时,肯定先完成了父类的初始化,

子类所有构造方法都会默认的访问父类中的无参构造方法

子类的每个构造方法,虽然一般没有写出,但都默认是super();

一些情况中,父类可能没有无参构造,子类就无法实现默认构造,

此时就可以通过自己利用super调用父类的带参构造方法,

也可通过this调用本类中其他构造方法

需要注意,this和super都要出现在第一条语句

(3)继承中类和对象初始化问题:

结合之前的代码块执行顺序:静态代码块》构造代码块》构造方法

子父类之间有分层初始化,子类创建引用时,加载父类后加载子类,

但此时并没有执行其他构造部分,只有当指向了new的对象时,

才进行了对象的初始化,先进行父类构造,再进行子类构造

例:

//父类三种代码块
class Fu {
	static {
		System.out.println("父类静态块");
	}
	{
		System.out.println("父类构造代码块");
	}
	public Fu () {
		System.out.println("父类构造方法");
	}
}
//子类继承和三种代码块
class Zi extends Fu {
	static {
		System.out.println("子类静态块");
	}
	{
		System.out.println("子类构造代码块");
	}
	public Zi () {
		System.out.println("子类构造方法");
	}
}

这两个类定义后,如果通过测试类去Zi z = new Zi();执行完毕后的结果为:

父类静态块
子类静态块
父类构造代码块
父类构造方法
子类构造代码块

子类构造方法

分析:执行这个语句先是左边创建一个引用,进行了类的初始化,从父到子,但并没有构造对象,只执行了两个类中的静态代码块,当new了一个对象之后,才从父类构造开始对对象进行初始化。

另一个例子:

class X {
	Y b = new Y();
	X () {
		System.out.print("X");
	}
}

class Y {
	Y () {
		System.out.print("Y");
	}
}

public class Z extends X{
	Y y = new Y();
	Z () {
		System.out.print("Z");
	}
	public static void main (String[] args) {
		new Z();
	}

}

这个程序输出结果是:

YXYZ

分析:从main方法进入后直接new子类对象,此时是先进行父类对象初始化,所以在父类中执行了构造输出YX

接着是回到子类对象初始化,输出了YZ。是对分层初始化的理解

刚开始可能会想的太多,认为像上面例子一样,进入父类初始化后先回到子类初始化再构造父类对象(可能想成YYXZ),但这里其实可以理解为

访问Y的部分就是构造代码块,虽然在构造外层,也是随着构造方法执行,而且这个例子是直接new的对象,并不像上面例子中先创建引用进行父类初始化,这里是直接执行到父类对象初始化完毕才进入子类进行对象初始化。

(4)方法重写:

子类中可以定义与父类中同名的方法,实现不同的功能,这个过程就是实现了重写

因为同名的东西,都是采用就近原则执行,所以才有了重写,但重写除了写新的功能,也可以通过super调用父类方法

这样既保留了父类中通用的属性方法,又添加了新的内容

注意事项:

父类私有方法无法被重写,因为本身无法继承

子类重写父类方法时,权限不能降低,开发中最好一致。

重写(override)与重载(overload):

前者是子类中与父类同名方法声明的现象

后者是同类中,对同名方法进行不同参数列表声明的现象

this与super:

this代表当前类的对象引用,super代表父类存储空间的标识

应用场景有访问成员变量、构造方法、成员方法。

final关键字:

除了不让子类继承的private方法,还存在一种只是不想让子类重写但允许其使用的情况

这时可以用final来修饰

除了修饰方法,修饰变量时,只能赋值一次,但这里需要理解的是

修饰基本数据类型,相当于将变量变成了常量,值是无法改变的

修饰引用类型时,只是地址值不能改变,值是可以改变的

比如有一个学生类,一个属性是name,那么final引用一个学生对象时,name是可以改变的,但这个引用不能指向另一个new的学生对象。

另外,final也可以修饰类,一般用于最底层的类,不再能继承。

9、多态:

(1)定义:同一对象在不同时刻体现不同的状态,比如可以引用一个动物变量指向一个猫或狗的对象。

多态的前提是继承,并且有方法的重写(虽然不是硬性要求,但如果没有,多态就没有了存在的意义)

提高了代码的扩展性,可以适应各种子类共性,也提高了代码的维护性(这里是因为继承的特点)

但需要使用子类特有功能时,可以向下转型为子类,只有确定能够强制转换时才能这样做

(2)特点:各成员的编译运行

成员变量:编译父类,运行父类

成员方法:编译父类,但运行时用的是子类,因为进行了方法的重写

静态方法:编译运行都是父类的,所以子父类同名的静态方法并不是真正意义的重写

(3)异常:

强制转换中转换了不能转的对象,编译时不会报错,因为符合规则,但运行时会出现异常

ClassCastException类转换异常,一般出现在向下转型

(4)例子:看程序写结果

public class Test3 {
	public static void main (String[] args) {
		A a = new B();
		a.show();
		
		B b = new C();
		b.show();
	}
}

class A {
	public void show () {
		show2();
	}
	public void show2 () {
		System.out.println("我");
	}
}

class B extends A {
	public void show2() {
		System.out.println("爱");
	}
}

class C extends B {
	public void show () {
		super.show();
	}
	public void show2 () {
		System.out.println("你");
	}
}

输出结果:爱你

分析:进入main方法,a.show();调用show2();而show2()在子类B中重写,输出为B中的show2();“爱”

接着b.show();运行看C中重写的show方法,而C中正好返回父类B中的show,但发现b中没有show,其实隐含了直接继承A的show方法

所以运行show2方法,而C中对show2进行了重写,输出的是“你”。

10、抽象:

(1)定义:父类中,许多成员不应该是具体的,而应该是给出拥有的特性,大多数让子类去重写,这时就要引入抽象,用关键字abstract定义抽象类

(2)注意事项:

抽象类中不一定所有成员都要抽象,但反过来只要有一个抽象成员,那么这个类一定要是抽象的

抽象方法不能有方法体,这里的理解是括号后直接接分号;,用大括号括起部分即使里面没有内容,也算一个空方法体。

抽象类继承是,子类必须重写所有抽象方法才能是一个具体的类,如果不重写就需要也定义为抽象类

但抽象类不能实例化,这样也就没有了意义

抽象类想要进行实例化,是以多态的方式通过具体子类进行实现。

(3)成员特点:

成员变量:可以是常量也可以是变量

构造方法:存在,虽然不能构造对象,但可以访问父类数据时需要

成员方法:可以抽象也可以非抽象;抽象必须重写,非抽象直接继承,提高代码复用性

这里可能会被问到一个问题:如果一个类是抽象类,但是没有抽象方法,意义何在?

答:禁止创建父类对象,让其只能通过子类多态实现方法调用。

(4)关键字共存问题:

private:冲突,因为private不允许重写,而abstract要求必须重写。

final:同样冲突,final是最终的,只能赋值一次,不能继承和重写,而抽象要求必须重写。

static:没意义,抽象方法是通过子类重写实现一些功能,而静态是直接以类名调用方法,这样调出来的是没有方法体的功能。

11、接口:

(1)虽然子类中可以继承父类,并且增加一些功能,但有些特性并不是所有子类都拥有,而又不方便在父类中定义。这时就引入了接口,关键字interface。以implements实现

(2)特点:

接口是抽象的,不能实例化,但也是能用子类以多态实现实例化。

接口的子类,可以是抽象类但没有意义,具体类要重写所有方法

成员变量:默认是常量,修饰符public static final,即使自己写其中一个也默认是三个

构造方法:接口没有构造方法,子类中构造方法默认继承与Object类

成员方法:只能是抽象方法,默认修饰符public abstract

(3)继承问题:

类与类:单继承,多层继承

类与接口:实现关系,可以多实现,间接实现多继承

即一个类可以继承一个父类的同时实现多个接口

接口与接口:这里可以多继承!

因为多继承类时,多个父类如果出现同名变量方法,则会错乱,不允许多继承

而接口里面都是抽象方法,变量也都默认成为常量,静态域只有自己使用。

(4)接口与抽象类的区别:

抽象类:成员变量可以是变量和常量,有构造方法,成员方法抽象与否都可以。

接口:只有常量,没有构造方法,成员方法只能抽象。

继承关系上结合(3)即可

设计理念的不同:

抽象类实现继承时,属于“is-a”关系,体现了共性

接口与类是实现功能,属于“like-a”关系,体现扩展性

(5)作为形参传递问题:

类:传入的是该类对象

抽象类:传入其子类对象

接口:传入实现该接口的类的对象

作为返回值时同理。

12、修饰符的综合总结:

(1)权限修饰符:

public,公用的,在不同包、不相关类中皆可访问

protect,保护的,同包内、相关子类中访问

默认(不写或者friendly),包内访问

private,私有的,只能在本类访问和使用

(2)状态修饰符:

static:静态的,修饰方法时可以直接用类名进行调用

final:最终的,只能赋值一次或给一次地址值

(3)抽象修饰符:abstract

(4)应用:

类:默认public,可以抽象abstract修饰,也可以在底层类用final修饰

成员变量:除了abstract,其他修饰符都可使用,private较多

构造方法:只能用权限修饰符,不能用状态修饰符和抽象修饰符

成员方法:根据功能不同,所有修饰符都可以使用

13、内部类:

(1)定义:定义在类中的类

内部类可以访问外部类的成员,包括私有成员

外部类访问内部类,需要创建对象

(2)分类:

成员内部类:在类的成员位置,一般设置为private后,给出其他公共的成员方法进行访问;可以用静态修饰,但此时只能访问静态成员

局部内部类:在类中的方法内。

例:如何输出30,20,10

public class Test2 {
	public static void main (String[] args) {
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	}
}

class Outer {
	public int num = 10;
	class Inner {
		public int num = 20;
		public void show() {
			int num = 30;
			System.out.println(?);
			System.out.println(??);
			System.out.println(???);
		}
	}
}

?:num;??:this.num;???:Outer.this.num;(也可以是new Outer().num)

第一个输出就近原则直接取方法内的num,第二个输出内部类的num直接用this关键字,第三个有可能会认为用super,但内部类与外部类是没有继承关系的,所以直接用Outer.this表示外部类对象再调用num。

(3)匿名内部类:(结合安卓理解)

前提:存在一个类或者接口

格式:new 类或接口名(){重写方法;};

理解:说是一个类,但实际上是一个继承了该类或接口的子类匿名对象,重写了方法。不需要单独写一个类去继承,在只需要调用少次方法时,这样更方便。体现的是多态的思想

例子:补全程序,输出“helloworld”

public class Test5 {
	public static void main (String[] args) {
		OuterClass.method().show();
	}
}

interface InterFace {
	void show ();
}
class OuterClass {
	//补全此处
}

分析:进入main方法,OuterClass直接以.号访问method方法,可以看出此方法是静态修饰

接着OuterClass.method()整体可以用点号访问show方法,说明返回的是一个InterFace对象,只有对象才能访问方法

需要注意的是接口中方法只用void修饰,但其实默认还是public abstract void,重写时不能忘了public

答案是:

public static InterFace method () {
		return new InterFace() {
			public void show () {
				System.out.println("helloworld");
			}
		};
	}

返回接口对象,声明中要加入InterFace,方法要返回其对象,但接口不能实例化,实质上返回的是子类对象,并直接重写了show方法输出结果,也是多态思想。安卓中按钮监听器都是这种写法。

14、eclipse常用快捷键:

ctrl + shift + o;快速导包

ctrl + shift + f;格式化代码

alt + /;输入提示,这里可以在设置中将所有字母都设置成提示关键字,默认只有点号

alt + shift + s;可以实现类中各种方法的重写,还要get与set方法的自动生成

15、Object类:

所有类的父类,所有类都直接或间接的继承这个类,只有一个无参构造方法

(1)主要方法:

hashCode();返回对象的hash码

getClass();返回对象的运行时类,可以用一个Class类引用接收,再调用getName方法获取类名(包括包名)

toString();返回该对象的字符串表示,默认下等于this.getClass().getName() + @ +Integer.toHexString(hashCode())

但这样似乎没什么用,所以一般都会在类中重写方法,输出成员变量值的组成,eclipse中也有自动重写功能

finalize();用于垃圾回收

clone();返回一个此对象副本,一般会进行重写

(2)重点说一下equals()方法:

经久不衰的面试问题,与==号的区别。

==号:在比较基本类型时,比较值是否相等;比较引用类型时,比较地址值是否相同

所以当比较两个成员变量赋值相同的对象时,还是返回false,因为地址不同

equals():查看源码,其中判断是this == obj;所以默认情况下就是==比较地址值,但意义不大

一般进行重写,比较变量的值是否相等,重写后再次比较两个变量相同对象时,返回true。

16、Scanner类:

(1)键盘录入,使用格式Scanner sc = new Scanner(System.in);需要导包

这里System类下有一个静态字段:public static final InputStream in;返回一个输入流。

所以使用scanner时用的构造方法是Scanner(InputStream source);

(2)常用方法:

hasNextXxx,判断是否有Xxx类型字段

nextXxx,接收Xxx类型字段,常用接收int与String

注意事项:

获取两个数据时,获取两个数字、获取两个字符串、先一个字符串再一个数字,都不会出现问题

但是如果先获取一个数字再获取一个字符串,那个字符串是接收不到的,因为我们需要用回车确认一个输入完毕

而输入完数字回车,获取字符串时直接将回车符作为了字符串获取

解决方案:重新创建一个scanner对象接收第二个字符串。或者把所有数据通过字符串获取,需要使用特定格式再转换。

17、String类:

(1)字符串就是多个字符组成的一串数据,是lang包下的类,不需要导包。

本质是字符串常量,不能更改,但这个不能更改的理解,要通过以下例子:

		String s = "hello";
		s += "world";
		System.out.println(s);

这里输出仍然是helloworld,但与不能更改并不矛盾,这个过程中“hello”这个字符串在方法区被创建,并赋给了s

之后“world”被创建,并且与“hello”拼接生成了“helloworld”再赋值给了s,这个过程实际产生了三个字符串

不能改变只是说第一个“hello”存在于方法区并没有被改变,而s只是引用,指向不同地址则输出不同结果

(2)结合==与equals问题:

例子:

		String s1 = new String("hello");
		String s2 = new String("hello");
		System.out.println(s1 == s2);// false
		System.out.println(s1.equals(s2));// true

new了两个对象,地址不同,内容相同,问题不大。

		String s3 = new String("hello");
		String s4 = "hello";
		System.out.println(s3 == s4);// false
		System.out.println(s3.equals(s4));// true

对象在堆中,s3指向堆内存中的对象地址,s4直接指向方法区中常量池的“hello”字符串地址,问题不大。

		String s5 = "hello";
		String s6 = "hello";
		System.out.println(s5 == s6);// true
		System.out.println(s5.equals(s6));// true

指向方法区内常量池中同一个字符串地址,内容也相同,问题不大。

		String s7 = "hello";
		String s8 = "world";
		String s9 = "helloworld";
		System.out.println(s9 == s7 + s8);//false
		System.out.println(s9.equals(s7 + s8));//true

这里s7 + s8,属于变量相加,先开辟了一块空间,虽然空间指向“helloworld”字符串,但s9此时指向的是新开辟的空间地址

		System.out.println(s9 == "hello" + "world");//***true
		System.out.println(s9.equals("hello" + "world"));//true

这里需要注意,此时没有变量,相当于后面两个字符串直接拼接,拼接后发现方法区常量池存在一个“helloworld”字符串

所以地址与直接指向该字符串的s9相同,返回true。以上每个equals都返回true是因为String类中重写了方法,比较内容。

(3)一些方法:

判断:

equalsIgnoreCase(String str)忽视大小写的比较内容是否相同

contains,判断是否包括连续的小字符串

startWith判断是否以指定字符开头

isEmpty();判断是否为空

这里需要注意,是判断内容是否为空,如果String s = null;则会产生空指针异常

因为null说明连对象都没有,根本无法调用方法。空串指的是“”。

获取:

length();返回字符串长度

charAt(index);返回指定索引的字符,反过来indexOf(char)可以查询指定字符出现的第一个索引

substring(start,end)截取字符串,包括start,不包括end。

转换:

getBytes返回一个字节数组

toCharArray转换为字符数组

valueOf(类型很多)将其他类型数组转换为字符串

其他:

replace替换字符

trim去除两边空格

compareTo,按字典顺序比较,返回第一个不同字母的ascii码差值,如果都相同,但长度不同,返回长度相减值

18、StringBuffer类:

(1)String类字符串是固定的,如果每次都进行字符串拼接,会占用太多空间

StringBuffer就解决了这个问题,是一个缓冲区,空间可以变化,做拼接会更节省资源

(2)构造方法:

StringBuffer()无参构造

StringBuffer(int capacity);指定容量,默认为16

StringBuffer(String str);指定内容

(3)成员方法:

length();实际字符串的长度

capacity();整个缓冲区容量

如果指定内容构造,这里容量是该字符串的length+16.

append();将任意类型添加到缓冲区,类似一个水杯,每次返回的是对象本身,例:

                StringBuffer sb = new StringBuffer();
		StringBuffer sb2 = sb.append("hello");//没有重新开空间 
		System.out.println(sb);//hello
		System.out.println(sb2);//hello
		System.out.println(sb == sb2);//true

insert();插入

deleteCharAt()删除指定位置字符,直接delete()传入start和end,可以删除范围内字符

reverse();反转功能

以上几个操作功能基本返回对象本身,操作完对象本身变化了

substring()返回的是字符串,截取后的字符串给一个String 引用,但自身不变

(4)与String的相互转换:

String转换为StringBuffer

构造方法中可以直接传入String

或者创建对象后,用append方法将字符串拼进去

StringBuffer转换为String

利用substring方法截取返回String

或者String构造中传入StringBuffer对象也是可以的,这个构造方法也存在

还可以用toString方法实现

(5)String、StringBuffer与StringBuilder区别:

String与后两个的区别在于,内容不可变,只能做字符串拼接,浪费空间

后两者都是可变缓冲区存放字符串

StringBuffer与StringBuilder使用方法几乎相同,但引入了一个概念

前者是线程安全的,同步的,效率较低

后者可看做前者的简易版,但不同步,不是线程安全,效率较高

所以单线程编程中,大多使用StringBuilder。

另外,后两者与数组的区别:

数组只能存放同一种类型的数据

StringBuffer可以存放不同类型,最后是一个字符串

19、排序与查找

回顾了简单的冒泡排序,选择排序和二分查找

冒泡排序:数组中两两对比,大的放在后面,比较结束后,最大元素在数组结尾

每轮比较结束后,只需比较到上一次索引-1的位置上

	public static void sortArray(int[] arr) {
		int tmp;
		// 外层控制排序次数
		for (int x = 0; x < arr.length - 1; x++) {
			// 注意数组越界,内层两两比较
			for (int i = 0; i < arr.length - 1 - x; i++) {
				if (arr[i] > arr[i + 1]) {
					tmp = arr[i];
					arr[i] = arr[i + 1];
					arr[i + 1] = tmp;
				}
			}
		}
		//不需要返回值
	}

选择排序:从第一个元素开始,与后面的每个元素比较,小的就交换到此位置,比较结束,最小元素在数组开头

每轮比较结束后,只需从下一个元素跟后面比较即可

	public static void sortSelect(int[] arr) {
		int tmp;
		// 外层拿每个索引
		for (int i = 0; i < arr.length - 1; i++) {
			// 内层与每个比较
			for (int j = i + 1; j < arr.length; j++) {
				if (arr[j] < arr[i]) {
					tmp = arr[i];
					arr[i] = arr[j];
					arr[j] = tmp;
				}
			}

		}
	}

二分查找:对于有序数组,可以使用二分查找,类似于猜数字游戏中,我们每次都会从范围中间值猜起。

	public static int binarySearch(int[] arr, int value) {
		int min = 0;//最小索引
		int max = arr.length - 1;//最大索引
		int mid = (min + max) / 2;//中间索引
		while (arr[mid] != value) {//没找到就持续比较
			if (arr[mid] > value) {
				max = mid - 1;//猜数字大了就在前半部分再猜
			} else if (arr[mid] < value) {
				min = mid + 1;//猜小了就在后半部分再猜
			}
			if (min > max) {
				return -1;//最小索引比最大索引还大,说明找不到了。直接跳出
			}
			mid = (min + max) / 2;//重新计算中间索引值
		}
		return mid;//找到则返回当前索引值
	}

20、Arrays类:

自己进行排序和查找,十分麻烦,所以可以使用自带的工具类Arrays

方法:(以类名直接调用)

toString数组转为字符串(数组直接.toString()返回的是地址值,Arrays.toString进行了重写)

sort();排序,这里源码使用的是快速排序

binarySearch();查看源码发现比上面设计要完善的多,并且优化的很好,考虑也更全面

比如以后自己开发,大多数方法中也要先进行null判断,防止空指针异常。

21、包装类:

(1)基本类型有时我们也想要去调用方法实现一些功能,而他们本身不能做到

所以Java对他们每个类型都提供了包装类,除了方便调用方法,还可以实现与字符串之间的转换

(2)以Integer为例

Integer(int value)将int转为包装类

Integer(String str)将字符串转成包装类,但字符串中必须是数字字符组成

int到String的转换

除了之前提的valueOf方法,可以以Integer为中介,调用toString进行转换

String s = Integer.toString(int i);

反过来,提供了一个方法,将字符串转为int型

Integer.parseInt(s);

(3)进制转换

toBinaryString()/toOctalString()/toHexString()分别可以转2、8、16进制

toString(int i , int radix)向任意进制转换,范围2-36进制

(4)自动拆装箱

Jdk5以后的新特性

原本需要一个Integer对象需要Integer i = new Integer(100);

而现在可以直接Integer i = 100;这里就是自动装箱

包括i += 100;这样的操作,原本是先进行拆箱,转为int型,计算完毕后再进行装箱。

本来的写法应该是 i = Integer.valueOf(i.intValue() + 100);

同样 Integer i = null;这样的操作是不行的,null没有对象,不能使用自动拆装箱中的方法。

(5)依旧是==与equals问题

                Integer i = new Integer(128);
		Integer ii = new Integer(128);
		
		System.out.println(i == ii);//false,对象不同
		System.out.println(i.equals(ii));//true

创建了不同的对象,地址不同,内容相同,问题不大。

		Integer i2 = 127;
		Integer ii2 = 127;
		
		System.out.println(i2 == ii2);//true,在常量池中,地址相同
		System.out.println(i2.equals(ii2));//true

在byte范围内,直接去常量池中值,两个地址相同,内容相同,问题不大。

		Integer i3 = 128;
		Integer ii3 = 128;
		
		System.out.println(i3 == ii3);//false,不在常量池中,创建了新对象
		System.out.println(i3.equals(ii3));//true

超出了byte范围(-128~127),会自动创建新的对象,地址不同,内容相同,需要注意。

其他类型的包装类,方法原理都类似,可以读源码得知。

22、正则表达式(Linux中也常用)

(1)定义:符合一定规则的字符串

String regex = “规则”;

(2)规则:

符号类:

x    表示字符本身

\\    表示\单个反斜杠符

\n    换行

\r    回车

字符类:

[abc]    表示a/b/c单个字符

[^abc]    表示除了abc以外的字符

[a-zA-Z]    表示所有字母

[0-9]    表示所有数字

预定义:

.    点号表示任意字符,如果需要的就是点号本身,用反斜杠 \.

\d    任意数字的另一种写法,大写表示除了数字,相当于[^0-9]

\w    任意字母和数字的写法[a-zA-Z_0-9],大写W同上。

边界:

^    表示一行的开头

$    表示一行的结尾

\b    表示单词边界

数量:

X?    表示x出现1或0次

X*    表示x出现0次或多次(包括一次)

X+    表示x出现1次以上

X[n]    表示x出现n次

X[n,]    至少n次    

X[m,n]    出现m到n次

(3)应用:

String类中有一些方法,传入规则后可以实现相应功能

matches(String regex)判断是否满足指定规则,返回boolean类型

split(regex)以特定规则进行拆分,返回一个字符串数组

replaceAll(regex,replacement)按特定规则,全部替换

(4)模式和匹配器:

Api(application programing interface)中给了一种使用正则表达式的标准格式

 * Pattern p = Pattern.compllie(regex);

 * Matcher m = p.matcher(s);

之后调用find()/group()等方法

23、Math工具类:

(1)一些常用的量与方法

public static final double PI

abs();取绝对值

cell();向上取整

floor();向下取整

max();min();最大最小

pow(a,b)返回a^b

random();常用的随机数

round();四舍五入

sqrt();取正平方根

(2)关于random方法,有一个小问题:取得任意范围的随机数

我们知道得到0~99之间随机数,就是Math.random() * 100;

其实同理的:

	public static int getRandom(int a, int b) {
		// int num = (int) (Math.random() * b) + a;这样不行
		int num = (int) (Math.random() * (b - a + 1)) + a;
		return num;
	}

我们可能容易忘记在乘以100时,我们获取的是从0开始的数,要记得减掉范围最小值。

(3)Random类

其实获取随机数,有一个专门的工具类,更加方便获取任意范围

构造方法除了空构造,还有一个传入种子的构造Random(long seed);

成员方法:

nextInt();获取int范围内的随机数

nextInt(int n);获取[0,n)之间的随机数

至于构造方法中的种子,我去尝试了一下,大概理解为

给定了种子的对象,在获取随机数时,只要范围不变,随机数也不变

但如果范围变了,会取到另一个随机数,多次执行,还是不变

种子改变,随机数也改变

总结一下就是,同一个种子在同范围内获取的随机数不变。

24、System类

(1)final修饰的类,不能实例化

(2)一些方法:

gc();运行垃圾回器,默认调用Object中的finalize()方法,一般来说,除了大量回收对象之外,让虚拟机自己决定何时回收即可

exit(int status);终止当前运行的虚拟机,传入非0则表示异常终止

currentTimeMillis();返回毫秒为单位的当前时间,默认从1970-1-1开始

arraycopy(Object scr,int srcPos,Object dest,int destPos,int length);

从指定数组的指定位置开始,复制到目标数组,也从指定位置开始,length代表复制个数,改变的只有第二个数组。

25、Big型类:

当Integer范围都不够用时,可以用BigInteger,基本的方法有加减乘除等操作

小数运算时,float和double容易丢失精度,产生类似0.99999+这样的数值

在金融项目中,这样的误差将会出现很大问题

所以Java提供了BigDecimal类,有一个构造方法可以传入字符串,进行数字运算,则不会产生误差

26、Date与Calendar类

(1)Date,表示时间,精确到毫秒

无参构造方法创建对象,输出是当前时间

传入毫秒数,则是将时间设置为从1970.1.1开始该毫秒数的时间。

(需要注意的是我们中国是东八区,有八个小时时差,默认时间从1970.1.1的8:00开始)

一些方法:

getTime();获取时间

setTime();设置时间

格式化:

提供了一个抽象类DateFormat实现日期与字符串的转换

由于是抽象类,需要用子类初始化对象调用方法

例子:

	public static void main(String[] args) throws ParseException {
		Date d = new Date();
		//Date 到 String 格式化
		SimpleDateFormat sdf = new SimpleDateFormat();//可以自定义格式
		String s = sdf.format(d);
		System.out.println(s);
		//String 到 Date 解析
		String ds = "2018-03-18 20:08:50";
		//解析时必须将格式写出,与给定字符串匹配
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date df = sdf2.parse(ds);//抛出异常
		System.out.println(df);	
	}

解析格式必须相同,并且需要抛出异常才能使用。

(2)Calendar,日历,是一个抽象类,获取当前时间,Calendar c = Calendar.getInstance();右边实际上是子类对象。

一些方法:

get(int field)返回需要的日历字段的值,字段都是静态变量,如Calendar.YEAR。

add(field,amount)对指定的日历字段值进行加减

set(year,month,date)方便的直接设置日期

一个小题:获取任意一年的2月有多少天

正常去做,可能要考虑闰年等因素。但有了Calendar,则只需要先得到某年的3月1日,再对日期进行-1操作,获取日期字段即可

		// 键盘录入年份
		Scanner sc = new Scanner(System.in);
		System.out.println("year:");
		int year = sc.nextInt();//获取想查询的年
		Calendar c = Calendar.getInstance();//随意创建一个日历对象
		//设置日期为3月1日,需要注意月份是从0开始到11计数
		c.set(year, 2, 1);
		c.add(Calendar.DATE, -1);//对查询年3月1日进行-1操作,即得到2月最后一天
		System.out.println(c.get(Calendar.DATE));//获取日期字段输出

这些大概是基础中的基础了,做个笔记。

后面就是集合框架,一直是我比较头疼的地方;还有IO流、多线程,反射等,都很重要

GUI可能就不会应用太多了,毕竟现在Java的窗口应用太少,都是服务器端开发和算法之类。

继续努力,过完基本知识,看web,学框架。

毕设是安卓,还得学学基础,完成简单功能。

以后走什么方向呢,总之把Java学好就行了吧~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值