本系列文章共分为五篇:
Java基础知识(一)基本数据类型、变量类型、修饰符、表达式、数组、Math类、分支循环、关键字、调用方式、BS/CS、Java SE/Java EE/Java ME、JDK与JRE
Java基础知识(二)字符串、Java常用包、四种引用、内存泄漏、深克隆与浅克隆、IO、语法糖
Java基础知识(三)面向对象、类和对象、封装继承多态、重写和重载、构造方法、内部类、包装类、对象实例化的四种方式
Java基础知识(四)异常、final/finally/finalize、可变参数、枚举、日期、反射、泛型
Java基础知识(五)网络编程、lambda表达式、Stream、自定义注解
Java语言的特点:
Java中,变量的目的是申请内存来存储值。也就是说,当一个变量被创建时,就需要在内存中申请空间,内存管理系统根据变量的类型为变量分配相应的存储空间,此时分配的空间只能用来存储该类型数据。
Java有两大数据类型:
1>内置数据类型
2>引用数据类型
Java中,共有八种基本类型:六种数字类型(四整型,二浮点型),一种字符类型,一种布尔类型。
整形数据分为4种:
1>byte
8位,有符号整数,范围为-27 ~ 27-1,默认值是0。
2>short
16位,有符号整数,范围为-215 ~ 215-1,默认值是0。
3>int
32位,有符号整数,范围为-231~ 231-1(约-21亿 ~ 21亿),默认值为0,一般整型变量默认为int型。
4>long
64位,有符号整数,范围为-263 ~ 263-1,默认值为0L。赋予long型变量初始值时,需要在数字后面加上L。
测试代码如下:
static byte b1;
static short s1;
static int i1;
static long l1;
public static void main(String[] args){
System.out.println("byte型变量默认值 :"+b1 );
System.out.println("short型变量默认值 :"+s1 );
System.out.println("int型变量默认值 :"+i1 );
System.out.println("long型变量默认值 :"+l1 );
}
测试结果:
byte型变量默认值 :0
short型变量默认值 :0
int型变量默认值 :0
long型变量默认值 :0
在进行整型数据运算时,要注意溢出的问题,比如以int型为例,Integer.MAX_VALUE+1的运算结果是Integer.MIN_VALUE
。
浮点型数据分为2种:
浮点型变量默认为double型
。测试代码如下:
static float f1;
static double d1;
public static void main(String[] args){
System.out.println("float型变量默认值 :"+f1 );
System.out.println("double型变量默认值 :"+d1 );
}
测试结果:
float型变量默认值 :0.0
double型变量默认值 :0.0
先看一个例子:
System.out.println(0.06+0.01);
System.out.println(2.0-1.1);
System.out.println(3.3*3);
System.out.println(3.3/3);
在我们的想象中,这4个语句的执行结果应该是0.07、0.9、9.9、1.1,但是事实却不是这样的,实际的结果是:
0.06999999999999999
0.8999999999999999
9.899999999999999
1.0999999999999999
从这个例子可以看出,其实浮点数的计算是可能不准确的,引用网上的一段话,float和double类型的使用局限:
1)单精度浮点型变量float可以处理6 ~ 7位有效数,双精度浮点型变量double可以处理15~16位有效数,在实际应用中,如果需要对更大或者更小的数进行运算和处理,这时候float和double就无能为力了。
2)float和double类型的主要设计目标是为了科学计算和工程计算,他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。
商业计算往往要求结果精确,这时候就需要用到BigDecimal
。BigDecimal的使用,示例如下:
public class DoubleTest {
public static void main(String[] args) {
System.out.println(add(0.06,0.01));
System.out.println(subtract(2.0,1.1));
System.out.println(multiply(3.3,3));
System.out.println(divide(3.3,3,1));
}
/* 加法运算*/
public static double add(double x, double y) {
BigDecimal vala = new BigDecimal(Double.toString(x));
BigDecimal valb = new BigDecimal(Double.toString(y));
return vala.add(valb).doubleValue();
}
/*减法运算*/
public static double subtract(double x, double y) {
BigDecimal vala = new BigDecimal(Double.toString(x));
BigDecimal valb = new BigDecimal(Double.toString(y));
return vala.subtract(valb).doubleValue();
}
/*乘法运算*/
public static double multiply(double x, double y) {
BigDecimal vala = new BigDecimal(Double.toString(x));
BigDecimal valb = new BigDecimal(Double.toString(y));
return vala.multiply(valb).doubleValue();
}
/*除法运算(结果进行四舍五入)*/
public static double divide(double x, double y, int scale) throws ArithmeticException {
if (scale < 0) {
throw new ArithmeticException("精确度不能小于0");
}
BigDecimal vala = new BigDecimal(Double.toString(x));
BigDecimal valb = new BigDecimal(Double.toString(y));
return vala.divide(valb, scale, BigDecimal.ROUND_HALF_EVEN).doubleValue();
}
}
此时的结果就和我们预想的一致了,如下:
0.07
0.9
9.9
1.1
boolean,1位,只有两个取值:true和false,默认值是false。
测试代码如下:
static boolean b1;
public static void main(String[] args){
System.out.println("boolean型变量默认值 :"+b1 );
}
测试结果:
boolean型变量默认值 :false
char,16位Unicode字符,范围为0 ~ 215-1(\u0000到\uffff),可存储任意字符,默认值是’u0000’。
一个char类型变量中可以存储一个中文汉字,因为char类型变量使用的默认编码格式是 Unicode,一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。
每种数据类型的取值范围可以通过其包装类来查看,如下:
类型 | 位数 | 最大值 | 最小值 |
---|---|---|---|
byte | Byte.SIZE | Byte.MAX_VALUE | Byte.MIN_VALUE |
short | Short.SIZE | Short.MAX_VALUE | Short.MIN_VALUE |
int | Integer.SIZE | Integer.MAX_VALUE | Integer.MIN_VALUE |
long | Long.SIZE | Long.MAX_VALUE | Long.MIN_VALUE |
float | Float.SIZE | Float.MAX_VALUE | Float.MIN_VALUE |
double | Double.SIZE | Double.MAX_VALUE | Double.MIN_VALUE |
char | Character.SIZE | Character.MAX_VALUE | Character.MIN_VALUE |
测试代码如下:
System.out.println("byte型变量位数:"+Byte.SIZE+",byte型变量最大值:"+Byte.MAX_VALUE+",byte型变量最小值:"+Byte.MIN_VALUE );
System.out.println("short型变量位数:"+Short.SIZE+",short型变量最大值:"+Short.MAX_VALUE+",short型变量最小值:"+Short.MIN_VALUE );
System.out.println("int型变量位数:"+Integer.SIZE+",int型变量最大值:"+Integer.MAX_VALUE+",int型变量最小值:"+Integer.MIN_VALUE );
System.out.println("long型变量位数:"+Long.SIZE+",long型变量最大值:"+Long.MAX_VALUE+",long型变量最小值:"+Long.MIN_VALUE );
System.out.println("float型变量位数:"+Float.SIZE+",float型变量最大值:"+Float.MAX_VALUE+",float型变量最小值:"+Float.MIN_VALUE );
System.out.println("double型变量位数:"+Double.SIZE+",double型变量最大值:"+Double.MAX_VALUE+",double型变量最小值:"+Double.MIN_VALUE );
System.out.println("char型变量位数:"+Character.SIZE+",char型变量最大值:"+Character.MAX_VALUE+",char型变量最小值:"+Character.MIN_VALUE );
测试结果:
byte型变量位数:8,byte型变量最大值:127,byte型变量最小值:-128
short型变量位数:16,short型变量最大值:32767,short型变量最小值:-32768
int型变量位数:32,int型变量最大值:2147483647,int型变量最小值:-2147483648
long型变量位数:64,long型变量最大值:9223372036854775807,long型变量最小值:-9223372036854775808
float型变量位数:32,float型变量最大值:3.4028235E38,float型变量最小值:1.4E-45
double型变量位数:64,double型变量最大值:1.7976931348623157E308,double型变量最小值:4.9E-324
char型变量位数:16,char型变量最大值:,char型变量最小值:
char类型比较特殊,Character.MAX_VALUE的值是’\uFFFF’;MIN_VALUE的值是’\u0000’。
包装类还可以用来实现基本数据类型之间的转换,每个包装类中总有xxValue方法,来获得其对应的简单数据类型。
String不是Java的基本数据类型,默认值为null。
其他数据类型转换为String的方式:
- X.toString()
- X+""
- String.valueOf(X)
此处以int型数据转换为String为例,测试代码如下:
Integer i1 = 1;
String str1 = i1.toString();
String str2 = i1+"";
String str3 = String.valueOf(i1);
System.out.println(str1+","+str2+","+str3);
测试结果:
1,1,1
String数据转换为其他简单类型的方式:
1>包装类的xxValue(),比如:Double.valueOf(“32.1”).doubleValue()。
2>包装类的静态parseXX方法,比如:Double d = Double.parseDouble(s)。
Java中,任何类型与字符串相加,都是进行拼接。原理为:先用String.valueOf()方法,再通过StringBuilder进行拼接
。
此处以int型数据转换为String为例,测试代码如下:
String str1 = "123";
int i1 = Integer.valueOf(str1).intValue();
int i2 = Integer.parseInt(str1);
System.out.println(i1+","+i2);
测试结果:
123,123
Java中,引用类型的变量类似于C/C++中的指针,引用类型指向一个对象,指向对象的变量是引用变量。引用变量在声明时被指定为一个特定类型,并且不允许更改。
对象、数组是引用数据类型,引用类型的默认值是null。
final修饰常量,为了便于识别,一般用大写字母表示。
Java常用转义字符:
符号 | \n | \r | \f | \b | \0 | \s | \t | " | ’ | \ |
---|---|---|---|---|---|---|---|---|---|---|
意义 | 换行 | 回车 | 换页 | 退格 | 空字符 | 字符串 | 制表符 | 双引号 | 单引号 | 反斜杠 |
Java中,整型、常量、字符型数据可以混合运算。不同类型的数据进行运算时,会先转换为同一类型(更高的数据类型),再进行运算。
各变量优先级如下:
低 ----------------------------------------------------> 高
byte,short,char—> int —> long—> float —> double
在8种基本数据类型中,char是比较特殊的一种类型,一共有2的16次方个数值。char
char num1 = 1;
int num2 = num1;
double num3 = num1;
short num4 = num1; //编译报错
byte num5 = num1; //编译报错
再用个更清晰的图来表示的话,如下:
数据类型转换时需要注意的规则:
1>不能对boolean类型进行类型转换。
2>把优先级高的转换为优先级低时必须使用强制类型转换。
3>转换过程中可能导致溢出或损失精度。
自动类型转换发生在转换前的数据类型的位数低于转换后的数据类型。
强制类型转换发生的条件是转换前后的数据类型必须是兼容的。
强制类型转换的格式为:(type)value。
类型转换可分为两种:隐式转换和显式转换。
从表示范围小的类型转换为表示范围大的类型
,可以直接转换,称为隐式转换。示例如下:
short m = 912;
int n = m;
因为int可以存储的范围要比short存储的范围大,所以short类型可以直接转换成int类型。
从表示范围大的类型转换为表示范围小的类型
,需要强制转换,称为显式转换。示例如下:
double m = 9.12;
int n = (int)m;
System.out.println(n); //9
从该例子可以看出,显式转换可能伴随着精度的丢失
。
在使用+=、-=等符号时,常常伴随着隐式类型转换的进行,如果参与运算的两个变量数据类型相同,一般不会有什么问题。如果两个变量数据类型不同,就需要注意一些潜在的问题了。
先看如下代码存不存在问题:
byte a=1;
a=a+4;
System.out.println(a);
上面代码粗看上去,好像没什么问题,实则不是。在执行语句’a=a+4’时,a是byte类型,而数字4默认是int 类型。在java中,在a+4时,会进行 自动类型转换 ,所以a+4 会转换成int类型。而变量a还是byte类型,将int 类型的a+4赋值给byte类型的a ,无疑会报错。
此时如果要消除错误,可以使用强制类型转换,示例如下:
byte a=1;
a=(byte)(a+4);
System.out.println(a);
也可以用如下方法修改:
byte a=1;
a+=1;
System.out.println(a);
依然先看如下代码存不存在问题:
byte a = 127;
byte b = 0;
b = a + b;
粗看之下好像也没问题,其实也有问题,报错如下:
此时建议将上述代码,修改为如下形式:
byte a = 127;
byte b = 0;
a += b;
上面现象产生的原因是:整型数据和整型数据进行运算的时候,如果两边的值都是小于或者等于int的,那么其结果就是int型。
此外还有一种情况,先看下面代码:
int a = 0;
long b = 10L;
a + b 的结果是long型,原因是:整型数据和整型数据进行运算的时候,如果有一边的长度超过int,那么运算结果就按照最长的长度。
当操作的变量涉及非int型变量时,建议使用'a += b',不用'b = a + b'
。
Java中的变量类型大致可分为3种:
类变量
,方法体之外的变量,用 static 修饰。实例变量
,方法体之外的变量,无 static 修饰。局部变量
:方法体中的变量。 局部变量的特征:
1>声明在方法、构造方法或者语句块中。
2>在方法、构造方法、或者语句块被执行的时候创建,执行完后,变量将会被销毁。
3>只在声明它的方法、构造方法或者语句块中可见,访问修饰符不能用于局部变量。
4>所占用的内存空间在栈。
5>没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
实例变量的特征:
1> 声明在一个类中,但在方法、构造方法和语句块之外。
2>当一个对象被实例化之后,每个实例变量的值就跟着确定。
3>实例变量在对象创建的时候创建,在对象被销毁的时候销毁。
4>访问修饰符可以修饰实例变量。
5>实例变量对于类中的方法、构造方法或者语句块是可见的。
6>实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。
7>实例变量可以直接通过变量名访问。
静态变量的特征:
1>在方法体之外,用static关键字声明。
2>静态变量的数量与该类创建的对象数量无关,只有一个。
3>静态变量储存在静态存储区。
4>静态变量在第一次被访问时创建,在程序结束时销毁。
5>默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。
6>可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
类变量被声明为public static final类型时,类变量名称一般建议使用大写字母,此时的作用就是与常量相同。
静态变量与实例变量的区别:
1>静态变量属于类,该类不生产对象,通过类名就可以调用静态变量。
2>实例变量属于该类的对象,必须产生该类对象,才能调用实例变量。
Java中修饰符主要分为:访问修饰符和非访问修饰符。
顾名思义,该类修饰符重要控制访问权限。不同修饰符的修饰对象,大致如下:
1>default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
2>private : 在同一类内可见。使用对象:变量、方法。
3>protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。
4>public : 对所有类可见。使用对象:类、接口、变量、方法。
不同修饰符的访问范围,如下:
修饰符 | 当前类 | 同包类 | 子类 | 其他类 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。
声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。也就是说,声明为private的方法和变量,都是当前类内部使用的,外部不能访问。
protected特征:
1>子类与基类在同一包中,被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问。
2>子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
访问控制和继承规则:
1>父类中声明为 public 的方法在子类中也必须为 public。
2>父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
3>父类中声明为 private 的方法,不能够被继承。
这三条规则体现的访问权限放大机制,即子类方法的方法访问权限>=父类的方法访问权限。
关于private、default、protected、public四种修饰符在平时开发中的使用,不太知道怎么使用的话,可以简单参考以下几条原则:
1>属性通常使用private封装起来
。
2>方法一般使用public用于被调用
。
3>会被子类继承的方法,通常使用protected
。
4>作用范围最小原则。即能用private就用private,不行就放大一级,用default,再不行就用protected,最后用public, 这样就能把数据尽量的封装起来
。
非访问修饰符,主要有static、final、abstract。
Java表达式是变量、常量、运算符、方法调用的序列,执行指定的计算并返回特定的值。
表达式一般是按运算符来划分的,表达式种类如下:
1>算术表达式
2>关系表达式
3>布尔逻辑表达式
4>位运算表达式
5>赋值表达式
6>条件表达式
+ - * / % ++ –
a++代表先使用,再+1,;–a代表先减1,再使用。测试代码如下:
for(int i=0;i<3;i++){
System.out.println(i);
}
测试结果:
0
1
2
由这个例子可以看出,i++这种自增或自减符号在运算表达式或变量后面的,是先使用,再变化。
== != > < >= <=
关系运算符比较的都是值,而非引用。
位运算应用于char、short、int、long型数据。
位运算方式:
1>&,都为1,结果则为1,否则为0。
2>|,都为0,结果为0,否则为1。
3>^,相同为0,不同为1。
4>~,取反。
5> <<,按位左移运算符,乘2的n次方。
6> >>,按位右移运算符,除2的n次方。
逻辑运算有三种:
1>&&,逻辑与运算,都为真结果则为真。
2>||,逻辑或运算符,有真则为真。
3>!,逻辑非运算符,取反。
&&为短路逻辑与运算符,即前面的判断条件为false时,不再进行后面条件的判断。
||为短路或运算符,即如果左边的运算结果已经是true,右边的语句不会再执行。
关于短路功能,测试代码如下:
int i=1;
if(i==2 && (i++)==2)
System.out.println("test");
System.out.println("i:"+i);
测试结果:
i:1
由这个例子可以看出:当&&前面的判断条件i==2不成立时,后面的判断语句也没有进行。
= += -= *= /= %= <<= >>= &= ^= |=
特殊赋值运算符(如:+=)可以在不改变原有数据类型的情况下实现运算,如果两个值数据类型不一致,会进行强制数据类型转换。测试代码如下:
int i=1;
i+=2;
System.out.println("i:"+i);
i+=2.0f;
System.out.println("i:"+i);
测试结果:
i:3
i:5
即三元运算符,形式为:x = (expression) ? trueVal : falseVal。测试代码如下:
int gradeA=98;
int gradeB=93;
int betterGrade=0;
betterGrade=gradeA>gradeB?gradeA:gradeB;
System.out.println("两个同学中较好的成绩是:"+betterGrade);
测试结果:
两个同学中较好的成绩是:98
此处借鉴网上的一篇博客:Java关键字(一)——instanceof
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:
boolean result = obj instanceof Class
其中obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
此处需要注意的是:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
instanceof 运算符只能用作对象的判断,所以下面两种写法都会编译报错:
int i = 0;
System.out.println(i instanceof Integer);
System.out.println(i instanceof Object);
Java中,数据分为基本数据类型和引用数据类型,null不属于这两种类型,自然不能算作引用类型。因此,在JavaSE中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回 false。即下面的写法,会返回false
:
System.out.println(null instanceof Integer);
这是最常用的情况,示例如下:
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);
此处以List接口和其数组实现ArrayList为例。以个人理解,instanceof 运算符比较的是instanceof符号前面的引用的对象是否后面接口的实现,示例如下:
ArrayList arrayList = new ArrayList();
System.out.println(arrayList instanceof List);//true
List list = new ArrayList();
System.out.println(list instanceof ArrayList);//true
该情况与上种情况比较类似,instanceof 运算符比较的是instanceof符号前面的引用的对象是否后面类或其子类,示例如下:
/*父类*/
public class Person {
}
/*子类*/
public class Man extends Person{
}
/*测试类*/
public class ClassTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Man();
Man m1 = new Man();
System.out.println(p1 instanceof Man);//false
System.out.println(p2 instanceof Man);//true
System.out.println(m1 instanceof Man);//true
}
}
在前面提过这样一句话“编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定”,这句话可以仍然用上面的Person类和Man类来说明,示例如下:
System.out.println(p1 instanceof String);//编译报错
System.out.println(p1 instanceof List);//运行时报错
System.out.println(p1 instanceof List<?>);//运行时报错
System.out.println(p1 instanceof List<Person>);//编译报错
先看Java SE 8中的实现,用伪代码表示为:
boolean result;
if (obj == null) {
result = false;
} else {
try {
T temp = (T) obj; // checkcast
result = true;
} catch (ClassCastException e) {
result = false;
}
}
在执行obj instanceof T运算时,首先,会检查obj的类型是否是引用类型或空类型,如果不是则会发生编译时错误;
其次,如果 obj 强制转换为 T 时发生编译错误,则关系表达式的 instanceof 同样会产生编译时错误;
最后,在运行时,如果 T 的值不为null,并且 obj 可以转换为 T 而不引发ClassCastException,则instanceof运算符的结果为true。 否则结果是false。
简单来说就是:如果 obj 不为 null 并且 (T) obj 不抛 ClassCastException 异常则该表达式值为 true ,否则值为 false。
Java运算符优先级(从上往下依次递减):
类别 | 操作符 | 运算顺序 |
---|---|---|
后缀 | () [] . (点操作符) | 左到右 |
一元 | + + – (此处指的是先执行自增或自减,再执行表达式) | 左到右 |
一元 | + + – (此处指的是先执行表达式,再执行自增或自减) ! 〜 | 右到左 |
乘性 | * /% | 左到右 |
加性 | + - | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > > = < < = | 左到右 |
相等 | == != | 左到右 |
按位与、异或、或 | &、^、 | |
逻辑与、或 | &&、 | |
条件 | ?: | 左到右 |
赋值 | = + = - = * = / =%= >> = << =&= ^ = | = |
或者看以下的表,运算符的优先级(从高到低):
如果在程序中,要改变运算顺序,可以使用()。
一个数组就代表在内存中开辟一段连续(意味着有序)
、用来存储相同类型
数据的空间,其特征如下:
数组的声明方式有两种,以int型数组举例:int[ ] array和int array[ ],一般用第一种方式。
数组的创建方式也有两种,以int型数组举例。
数组的遍历方式也有两种,以int型数组举例。
for(int i=0;i<array.length;i++)
System.out.println(array[i]+" ");
for(int arr:array)
System.out.println(arr+" ");
对数组进行复制的方法,常常有System.arraycopy和使用Arrays工具类两种方法。此处就介绍第一种,具体的方法是:
System.arraycopy(src, srcPos, dest, destPos, length)
src: 源数组
srcPos: 从源数组复制数据的起始位置
dest: 目标数组
destPos: 复制到目标数组的起始位置
length: 复制的长度
Math类包含用于执行基本数学运算的方法。常用方法有:三角函数类方法和数值计算类方法。
double d = 10.6;
float f = -95;
System.out.println(Math.ceil(d)); //11.0
System.out.println(Math.ceil(f)); //-95.0
double d = 10.6;
float f = -95;
System.out.println(Math.floor(d)); //10.0
System.out.println(Math.floor(f)); //-95.0
System.out.printf("log(%.3f) 为 %.3f%n", Math.E*Math.E,Math.log(Math.E*Math.E));
结果为:
log(7.389) 为 2.000
System.out.printf("log(%.3f) 为 %.3f%n", 100.0,Math.log10(100.0));
结果为:
log(100.000) 为 2.000
double d1 = 10.0;
double d2 = 2.0;
System.out.println(Math.pow(d1,d2)); //100.0
System.out.println(Math.round(11.5)); //12
System.out.println(Math.round(-11.5)); //-11
Arrays是一个封装好一些对数组操作的类,其中的public方法都是静态的,主要有以下几大类:
该类方法的实现是二分搜索
,例如在int数组中搜索某个值,方法具体代码如下: public static int binarySearch(int[] array, int startIndex, int endIndex, int value) {
checkBinarySearchBounds(startIndex, endIndex, array.length);
int lo = startIndex;
int hi = endIndex - 1;
while (lo <= hi) {
int mid = (lo + hi) >>> 1;
int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
public static void fill(int[] array, int start, int end, int value) {
Arrays.checkStartAndEnd(array.length, start, end);
for (int i = start; i < end; i++) {
array[i] = value;
}
}
public static int hashCode(int[] array) {
if (array == null) {
return 0;
}
int hashCode = 1;
for (int element : array) {
// the hash code value for integer value is integer value itself
hashCode = 31 * hashCode + element;
}
return hashCode;
}
用到的排序方式是快速排序
。示例代码如下: public static void sort(int[] array, int start, int end) {
DualPivotQuicksort.sort(array, start, end);
}
public static int[] copyOf(int[] original, int newLength) {
if (newLength < 0) {
throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
int[] datas = {
1,2,3,4,5,6};
List list = Arrays.asList(datas);
Collections类是针对集合操作的工具类。
public class Collections extends Object
常见方法:
Collections.sort(list);
Collections.sort(list,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if(o1.length()>o2.length()){
return 1;
}else if(o1.length()<o2.length()){
return -1;
}else{
return 0;
}
}
});
Java中,除了普通的顺序结构外,特殊的结构有三种:
if、if-else、if-else if-else结构适合某个变量值在连续区间
情况下的语句执行。
switch基本语法:
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
switch-case结构与多种if结构作用相似,但是有局限性,其特点如下:
等值判断
。switch使用的测试代码如下:
int grade=80;
switch(grade){
case 60:
case 70:
System.out.println("你的成绩为60或70分");
break;
case 80:
System.out.println("你的成绩为80分");
break;
default:
System.out.println("你的成绩为60、70、80之外的分数");
break;
}
Java中循环结构有两种:
for循环一般用于循环次数已知的情况
。while(判断条件){
循环体
}
do{
循环体
}while(判断语句);
do-while先执行循环体语句,然后进行判断,也就是无论如何会先执行一次循环体语句。
跳转语句有三种:break、continue、return。此外,异常处理也可以改变程序执行流程。
break有以下作用:
1>在switch中,用来终止一个语句序列。
2>用来退出一个循环。
continue只能用来for、while、do while循环中,用于跳过当前循环,直接进行下一次循环。
return语句使程序控制返回到调用它的地方,也就是:
1)返回方法的返回值;
2)终止当前程序。
关于break、continue、return的使用,可以通过一段测试代码来查看其具体使用,测试代码如下:
public class SkipTest {
public static void main(String[] args) {
int result = sum(10);
System.out.println("所求的数字之和是:"+result);
}
private static int sum(int num){
int s = 0;
for(int i=1;i<=num;i++){
if(i==3)
continue;
System.out.println("i:"+i);
s=s+i;
if(i==(num-1))
break;
}
return s;
}
}
这段代码的测试结果是:
i:1
i:2
i:4
i:5
i:6
i:7
i:8
i:9
所求的数字之和是:42
这段代码是一个序列的数字之和,不过不需要+3,所以在i == 3时,使用continue,表示跳过此次循环,进入下一次循环。我们也不希望+10,所以在i == (num-1)时,使用了break,直接跳出循环。return的作用就是返回到调用该方法的地方,有返回值的话将返回值返回,无返回值的话则不用返回。
Java关键字的标准定义是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。
java关键字共53个,包含两个保留字const,goto,具体如下:
关键字 | 意义 |
---|---|
abstract | 抽象 |
assert | 断言assert是jdk1.4引入的;jvm断言默认是关闭的;断言只适用复杂的调式过程;断言一般用于程序执行结构的判断,千万不要让断言处理业务流程 |
boolean | 布尔类型,值为true或false |
break | 结束一个语句块的执行,常用于switch…case和循环语句中 |
byte | 字节类型 |
case | 用于switch…case语句中,表示其中一个分支 |
catch | 用于try…catch语句中,用来捕捉异常 |
char | 字符类型 |
class | 类 |
const | 保留关键字 |
continue | 结束本次循环,进行下一次循环,主要用于for、while循环中 |
default | 用于switch…case语句中,表示匹配不上任何case的情况 |
do | 用于do…while循环中,其后是要默认执行一次的语句库 |
double | 双精度浮点类型 |
else | 表示条件不成立的情况 |
enum | 枚举 |
extends | 继承,用于类和类、接口和接口之间 |
final | 不可修改 |
finally | 用于try…catch语句中,用来处理不论是否捕捉到异常,都执行的语句 |
float | 单精度浮点类型 |
for | for循环,常用于有序序列的遍历 |
goto | 保留关键字 |
if | 某种条件的判定 |
implements | 实现接口,即实现接口文件中定义的空方法 |
import | 导入其他package中的类,用于跨包间的代码调用 |
instanceof | 判断某个对象是否属于某种类型 |
int | 整型数据类型 |
interface | 接口,声明一些空方法,待实现该接口的类去实现 |
long | 长整型数据类型 |
native | 代表调用本地C语言实现的接口 |
new | 创建对象 |
package | java语言中组织代码的一个单位 |
private | 私用,代表最低的访问优先级 |
protected | 被保护的,代表有继承等关联关系等的访问优先级 |
public | 公共的,代表最高访问优先级 |
return | 返回,结束方法 |
short | 短整型数据类型 |
static | 静态 |
super | 表示父类 |
switch | 多条件分支判定 |
synchronized | 同步,代表要保证线程安全 |
this | 表示当前类 |
throw | 抛出某个具体异常 |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
transient | 表示不需要被序列化 |
try | 用于try…catch语句 |
void | 表示该方法无返回值 |
volatile | 表明两个或者多个变量必须同步地发生变化 |
while | 用于while和do…while循环语句中 |
abstract代表抽象,可以修饰类和方法。
final代表最终的、不可更改的,可以修饰变量、方法和类。
final修饰变量时, 如果引用为基本数据类型,则该引用为常量,该值无法修改(更准确的说法:该变量只有一次赋值的机会);如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改;如果引用时类的成员变量,则必须当场赋值,否则编译会报错。
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
final修饰方法时,表示该方法无法被子类重写,但是可以被继承。
final修饰类时,表示该类无法被继承,同时类中的所有成员方法都会被隐式定义为final方法(只有在需要确保类中的所有方法都不被重写时才使用final修饰类)。
static关键字主要用来修饰成员变量和成员方法,在《Java编程思想》中对static关键字有如下阐述:
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。
简言之,使用static的目的是为了在不创建对象的前提下来调用方法/变量
。
static修饰方法时,该方法称为静态方法
。在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
static修饰变量时,称为静态变量
。静态变量在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。
除了修饰变量和方法外,static还可以修饰代码块,称为静态代码块
。一个类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。
static是不允许用来修饰局部变量。
静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
此处提一下类的初始化顺序:
关于该初始化顺序,可以通过如下测试代码来验证。父类测试代码如下:
package Basic;
public class BaseClass {
/*父类静态变量*/
public static String baseStaticVariable = "父类-->静态变量";
/*父类普通变量*/
public String baseVariable = "父类-->普通变量";
/*静态代码块*/
static{
System.out.println(baseStaticVariable);
System.out.println("父类-->静态代码块");
}
/*父类构造函数*/
public BaseClass() {
System.out.println("父类-->构造函数");
}
/*普通代码块*/
{
System.out.println(baseVariable);
System.out.println("父类-->普通代码块");
}
}
子类测试代码如下:
package Basic;
public class SubClass extends BaseClass{
/*子类静态变量*/
public static String subStaticVariable = "子类-->静态变量";
/*子类普通变量*/
public String subVariable = "子类-->普通变量";
public static void main(String[] args) {
new SubClass();
}
/*静态代码块*/
static {
System.out.println(subStaticVariable);
System.out.println("子类-->静态代码块");
}
/*普通代码块*/
{
System.out.println(subVariable);
System.out.println("子类-->普通代码块");
}
/*子类构造函数*/
public SubClass() {
System.out.println("子类-->构造函数");
}
}
测试结果如下:
父类–>静态变量
父类–>静态代码块
子类–>静态变量
子类–>静态代码块
父类–>普通变量
父类–>普通代码块
父类–>构造函数
子类–>普通变量
子类–>普通代码块
子类–>构造函数
super用于在子类中指代父类对象。super的三种使用情况:访问父类的方法;调用父类构造方法;访问父类中的隐藏成员变量。
调用父类构造方法的两种情况:
父类只有带参构造器(无参构造器没有),子类必须有相同参数的构造方法。子类必须有相同参数的构造方法,并且还需要调用super(参数)。
super表示父类,this表示当前对象。
this访问本类中的属性,如果本类没有此属性则从父类中继续查找。
this访问本类中的方法,如果本类没有此方法则从父类中继续查找。
this调用本类构造,必须放在构造方法的首行。super调用父类构造,必须放在子类构造方法首行。
在对拥有父类的子类进行初始化时,父类的构造方法也会执行,且优先于子类的构造函数执行
;因为每一个子类的构造函数中的第一行都有一条默认的隐式语句super();(如果子类的构造方法中没有手动调用父类的构造方法,则会默认调用父类的无参构造方法)可以由自己命名的地方都称为标识符。例如,对于常量、变量、函数、语句块、类、项目等都需要一个名字,这些我们都统统称为标识符。
要了解传值调用和传引用调用,需要先明白形参和实参。
传值调用都是传递的基本类型,示例代码如下:
public class BasicTest1 {
public static void main(String[] args) {
int num = 1;
System.out.println("形参初始值:"+num);
print(num);
System.out.println("形参最终值:"+num);
}
public static void print(int number){
number++;
System.out.println("形参修改值:"+number);
}
}
测试结果:
形参初始值:1
形参修改值:2
形参最终值:1
在该例子中,main方法里的num是形参,print方法体内的number是实参。
从这个例子可以看出,虽然方法体print内的number值改变了,但是main方法内的num值仍然是原来的值。这就证明了:传值调用时,方法不会改变实参的值。
传引用调用都是传递的对象类型,示例代码如下:
public class BasicTest2 {
private String name;
public BasicTest2(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public static void main(String[] args) {
BasicTest2 basicTest2 = new BasicTest2("hello");
test(basicTest2);
System.out.println(basicTest2.getName());
}
public static void test(BasicTest2 basicTest2){
basicTest2.setName("hello world");
}
}
测试结果如下:
hello world
从这个例子可以看出:传引用调用时,可以改变对象的属性,但属性的引用是的不变的。
类别 | 传值调用 | 传引用调用 |
---|---|---|
参数类型 | 基本类型数据 | 对象 |
是否改变原始值 | 改变的是形参的值,并没有改变实参的值。同时,这个传递是单向的,形参不能传递回实参 | 对对象做何种操作,都不会改变实参对象的引用,但是如果改变了对象的内容,就会改变实参对象的内容 |
接收值 | 方法其实接收的是参数的一个副本 | 方法接收的是原始值的内存地址,而不是值的副本 |
同时,还有一句比较具有迷惑性的话:“在Java里面参数传递都是按值传递”。这句话可以这样理解:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
BS架构,即浏览器----服务器(Browser/Server)结构,是目前应用系统的发展方向。BS是伴随着Internet技术的兴起,对C/S架构的改进,为了区别于传统的C/S 模式,特意称为B/S模式。在这种结构下,通过W3浏览器来进入工作界面,极少部分事务逻辑在前端(Browser)实现,主要事务逻辑在服务器端(Server)实现,形成三层(3-tier)结构。
BS的主要特点是分布性强、维护方便、开发简单且共享性强、总体拥有成本低。但数据安全性问题、对服务器要求过高、数据传输速度慢、软件的个性化特点明显降低,难以实现传统模式下的特殊功能要求。它是瘦客户端。
CS架构,即客户端----服务器(Client/Server)结构。C/S结构在技术上很成熟,它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。因为客户端要负责绝大多数的业务逻辑和UI展示,又称为胖客户端。
C/S 架构是一种典型的两层架构,其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据库服务器端,客户端通过数据库连接访问服务器端的数据;另一种是Socket服务器端,服务器端的程序通过Socket与客户端的程序通信。
Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java Platform,Enterprise Edition(Java EE)提供基础。
Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web 2.0 应用程序。
Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。
Java SE/Java EE/Java ME的区别:
J2EE, 主要做企业应用
, 比如公司网站, 企业解决方案等。简单来说,JDK是Java开发运行环境,JDK包括Java程序设计语言、Jaa虚拟机、Java API类库,开发Java程序的话需要安装JDK;JREJava运行环境,如果你不需要开发只需要运行Java程序,那么你可以只安装JRE。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
1、if, for, do, while, case, switch, default 等语句自占一行,且 if, for, do, while, switch, case 等语句的执行语句无论多少都要加括号{}。 ×
2、包的注释内容要求包括:简述本包的作用、详细描述本包的内容、产品模块名称和版本、公司版权、生成日期等。 ×
3、类注释部分,描述部分说明该类或者接口的功能、作用、使用方法和注意事项,每次修改后增加作者、新版本号和当天的日期,@since 表示从那个版本开始就有这个类或者接口,@deprecated 表示不建议使用该类或者接口。×
4、对于方法内部用 throw 语句抛出的异常,必须在方法的注释中标明;对于所调用的其他方法所抛出的异常,在注释中要求说明所有的异常;对于非RuntimeException,即 throws子句声明会抛出的异常,必须在方法的注释中标明。×
5、类名和接口使用完整的英文单词描述,每个英文单词的首字母使用大写、其余字母使用小写的大小写混合法。×
6、com.huawei.四级部门名称.项目名称,符合包命名规范。 √
7、不能用异常来做一般流程处理的方式,不要过多地使用异常,异常的处理效率比条件分支低,而且异常的跳转流程难以预测。√
8、划分类的时候,应该尽量把逻辑处理、数据和显示分离,实现类功能的多样化。×
9、一个方法不应抛出太多类型的异常,如果程序中需要分类处理异常,则将异常根据分类组织成继承关系。 √
10、switch 语句中的 case 关键字要和后面的常量保持一个空格;如果有特殊的需要要在switch 语句中定义 case 以外的标签,需要在注释中说明。 ×
11、没有被覆盖的友好方法和没有子类的友好类应该定义成 final。√
12、简单的类可以通过名字比较两个对象的类,推荐使用 getClass()或者 instanceof()。×
13、不要调用 Thread 类的 resume(), suspend(),sleep(), stop() 方法。×
14、判断方法是否是重载,只关注方法名、参数个数、参数类型,不关注方法返回值;√