计算机不能直接理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序。
把高级语言翻译成机器语言的两种方式:一种是编译,一种是解释。
编译型语言
编译型语言执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,比如exe文件,以后要运行的话就不用重新翻译了,直接使用编译的结果,因为翻译只做了一次,运行时不需要翻译,所以编译型语言的程序执行效率高,但也不能一概而论,部分解释型语言的解释器通过在运行时动态优化代码,甚至能够使解释型语言的性能超过编译型语言。
最典型的例子就是C、C++。
解释型语言
解释型语言的程序不需要编译,在运行程序的时候才翻译,比如basic语言,专门有一个解释器能够直接执行basic程序,每个语句都是执行的时候才翻译。这样解释性语言每执行一次就要翻译一次,效率比较低。
但是,随着Java等基于虚拟机的语言的兴起,我们又不能把语言纯粹地分成解释型和编译型这两种。
用Java来举例,Java首先是通过编译器编译成字节码文件,然后在运行时通过解释器给解释成机器文件。所以我们说Java是一种先编译后解释的语言。
Java源文件需要编译成.class文件才能在JVM中运行,而.class中的字节码首先会被JVM解释一遍,然后即时编译(JIT,Just-In-Time)。实际上,JVM字节码更像是中途的驿站,是一种从人类可读的源码向机器码过度的中间状态,用编译原理术语来讲,字节码实际上是一种中间语言形态(IL,IntermediateLanguage),不是真正的机器码。也就是说,将Java源码变成字节码的过程实际上不是C或者C++程序员所理解的那种编译。Java所谓的编译javac也不同于gcc,实际上它只是一个针对Java源码程程类文件的工具。Java体系中真正的编译是JIT,有人说Java是“动态编译”,指的是JIT的运行时编译,而不是构建时创建类文件的过程。
此外,还有一个我们经常提到的“脚本语言”的概念,一般来说可以归为解释型语言的范畴。
脚本语言又被称为动态语言,通常以文本(如ASCII)保存,在被调用时通常是解释执行而非编译。脚本语言的主要特征是:程序代码即是脚本程序,亦是最终可执行文件。
显然,java不是脚本语言,典型的脚本语言有JavaScript、Python、Perl、Ruby等。
Java标识符
Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于Java标识符,有以下几点需要注意:
所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划线(_)开始
首字符之后可以是任何字符的组合
关键字不能用作标识符
标识符是大小写敏感的
合法标识符举例:age、$salary、_value、__1_value
非法标识符举例:123abc、-salary
Java修饰符
Java语言提供了很多修饰符,主要分为以下两类
访问修饰符
修饰符用来定义类、方法或者变量,通常放在语句的最前端。我们通过下面的例子来说明:
publicclassclassName{//...}privatebooleanmyFlag;staticfinaldoubleweeks=9.5;protectedstaticfinalintBOXWIDTH=42;publicstaticvoidmain(String[]arguments){//方法体}访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。
1、默认的,也称为default,在同一包内可见,不使用任何修饰符。
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为publicstaticfinal,而接口里的方法默认情况下访问权限为public。
2、私有的,以private修饰符指定,在同一类内可见。
私有访问修饰符是最严格的访问级别,所以被声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。
3、共有的,以public修饰符指定,对所有类可见。
被声明为public的类、方法、构造方法和接口能够被任何其他类访问。如果几个相互访问的public类分布在不用的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
4、受保护的,以protected修饰符指定,对同一包内的类和所有子类可见。
被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。Protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected。子类能访问Protected修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。
请注意以下方法继承的规则:
父类中声明为public的方法在子类中也必须为public。
父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。
父类中默认修饰符声明的方法,能够在子类中声明为private。
父类中声明为private的方法,不能够被继承。
非访问修饰符
Static修饰符
静态变量:Static关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。静态变量也被称为类变量。局部变量能被声明为static变量。
静态方法:Static关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
Final修饰符
Final变量:Final变量能被显式地初始化并且只能初始化一次。被声明为final的对象的引用不能指向不同的对象。但是final对象里的数据可以被改变。也就是说final对象的引用不能改变,但是里面的值可以改变。
Final修饰符通常和static修饰符一起使用来创建类常量。
Final方法:类中的Final方法可以被子类继承,但是不能被子类修改。声明final方法的主要目的是防止该方法的内容被修改。
Final类:Final类不能被继承,没有类能够继承final类的任何特性。
Abstract修饰符
抽象类:抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。一个类不能同时被abstract和final修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
抽象类可以包含抽象方法和非抽象方法。
抽象方法:抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成final和strict。
任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
Synchronized修饰符
Synchronized关键字声明的方法同一时间只能被一个线程访问。Synchronized修饰符可以应用于四个访问修饰符。
Transient修饰符
序列化的对象包含被transient修饰的实例变量时,java虚拟机(JVM)跳过该特定的变量。该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
volatile修饰符
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。一个volatile对象引用可能是null。
Java变量
Java中主要有如下几种类型的变量
局部变量
只在特定的过程或函数中可以访问的变量,被称为局部变量。与局部变量相对应的,是全局变量。全局变量就是从定义的位置起,作用域覆盖整个程序范围的变量。
局部变量可以和全局变量重名,但是局部变量会屏蔽全局变量。在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。
一旦退出方法,构造函数或块中的变量将被销毁。
访问修饰符不能用于局部变量。
局部变量在堆栈级别内部实现。
局部变量要先赋值,再进行运算,而实例变量均已经赋初值。(这是局部变量和实例变量的一大区别)
全局变量(成员变量)
其中实例变量、类变量、常量都是属于成员变量的,成员变量又被称为全局变量
java类的成员变量有俩种:一种是被static关键字修饰的变量,叫类变量或者静态变量;另一种没有static修饰,为实例变量。
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
静态变量均有默认值,数字默认为0,布尔值默认为false,对象默认为null
publicclassTest{//以下四个变量都是成员变量(全局变量)intnum;//属于基本数据类型的实例变量Integerobj;//属于引用数据类型的实例变量staticcharch='A';//类变量(静态变量)finaldoubleconstant=12.6;//属于常量的实例变量publicvoiddisplay(){//以下两个属于该方法的局部变量,必须先赋值才能使用,作用范围仅限于函数体之内intage=1;Stringname="Tom";System.out.println("name:"+name+";age:"+age);}publicstaticvoidmain(String[]args){System.out.println(num);//这句会报错,因为num变量属于实例变量,只有将Test实例化之后才能使用System.out.println(Test.ch);//这句则不会报错,因为ch属于类变量,不用实例化Test也能使用Testtest=newTest();System.out.println(test.num);//num为基本数据类型,打印出的默认值为0System.out.println(test.obj);//obj为引用数据类型,打印出的默认值为null}}
Java数据类型
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
基本数据类型
注意:下面提到的默认值都是对于全局变量而言的,java平台不会给局部变量赋予默认值,使用没有赋值的局部变量会报错。
Java语言提供了八种基本类型:六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
byte:
byte数据类型是8位、有符号的,以二进制补码表示的整数;
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128(-27)
最大值:Byte.MAX_VALUE=127(27-1)
默认值:0;
byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一;
例子:bytea=100,byteb=-50。
short:
short数据类型是16位、有符号的以二进制补码表示的整数
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768(-215)
最大值:Short.MAX_VALUE=32767(215-1)
Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一;
默认值:0;
例子:shorts=1000,shortr=-20000。
int:
int数据类型是32位、有符号的以二进制补码表示的整数;
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648(-231)
最大值:Integer.MAX_VALUE=2147483647(231-1)
一般地整型变量默认为int类型;
默认值:0;
例子:inta=100000,intb=-200000。
long:
long数据类型是64位、有符号的以二进制补码表示的整数;
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808(-263)
最大值:Long.MAX_VALUE=9223372036854775807(263-1)
这种类型主要使用在需要比较大整数的系统上;
默认值:0L;
例子:longa=100000L,intb=-200000L。
float:
float数据类型是单精度、32位、符合IEEE754标准的浮点数;
float在储存大型浮点数组的时候可节省内存空间;
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
默认值:0.0f;
浮点数不能用来表示精确的值,如货币;
例子:floatf1=234.5f。
double:
double数据类型是双精度、64位、符合IEEE754标准的浮点数;
浮点数的默认类型为double类型;
double类型同样不能表示精确的值,如货币;
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
默认值: 0.0f;
例子:doubled1=123.4。
boolean:
boolean数据类型表示一位的信息;
只有两个取值:true和false;
这种类型只作为一种标志来记录true/false情况;
默认值是false;
例子:booleanone=true。
char:
char类型是一个单一的16位Unicode字符;
包装类:java.lang.Character
最小值是’\u0000’(即为0);
最大值是’\uffff’(即为65,535);
char数据类型可以储存任何字符;
例子:charletter=‘A’。
实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类java.lang.Void,不过我们无法直接对它们进行操作。
String并不是基本数据类型,但是我们可以把它当做基本类型来用
java的整型变量(byte、short、int、long)都可以用八进制、十六进制来表示,八进制必须以0为前缀,十六进制必须以0x为前缀,比如十进制的26分别用八进制、十六进制来表示:
八进制:032
十六进制:0x1a
引用数据类型
引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
String、数组都是引用数据类型。
所有引用类型的默认值都是null。
一个引用变量可以用来引用与任何与之兼容的类型。
例子:Animalanimal=newAnimal(“giraffe”)。
字符串和数组类型应用很广,从某种程度上来讲,可以将之看作基本数据类型
字符串和数组都可以不用new关键字来创建:
Stringstr="StringTest";
int[]arr={1,2,3}; //intarr[]={1,2,3}等价
也可以显式地创建:
Stringstr=newString(“StringTest”);
int[]arr=newint[]{1,2,3};
自动装箱与拆箱(AutoboxingandUnboxing)
自动装箱是java编译器在基本数据类型跟与之对应的包装类之间的自动转换。比如,将一个int类型的数据转换成Integer就叫自动装箱,与之相反的转换则成为自动拆箱。
例如:
Characterch=‘a’;
//实际上相当于执行代码:Character ch= newCharacter(‘a’);
再例如:
List<Integer>li=newArrayList<>();for(inti=1;i<50;i+=2){li.add(i);}
代码中List容器内的元素声明为Integer类型,而我们加入的却是int类型,但是编译器并不会报错,是因为编译器自动将int转换成了Integer类型,编译器在运行时将上面的代码转化为下面的形式:
List<Integer>li=newArrayList<>();for(inti=1;i<50;i+=2){li.add(Integer.valueOf(i));}
同样地,编译器也会在需要的时候将包装类型自动转换成与之对应的基本类型。例如:
publicstaticintsumEven(List<Integer>li){intsum=0;for(Integeri:li)if(i%2==0)sum+=i;returnsum;}
我们知道,“%”与“+=”运算符是不能应用于Integer类型的,只适用于基本类型,但是上面的代码不会报错,就是因为编译器将Integer转换成了int类型,代码转换成了下面的形式:
publicstaticintsumEven(List<Integer>li){intsum=0;for(Integeri:li)if(i.intValue()%2==0)sum+=i.intValue();returnsum;}java的自动装箱与拆箱在以下两种情况下会发生:
(1)传入方法的实际参数类型与方法中声明的参数类型不一致时
(2)变量的数据类型与赋值的数据类型不一致
这种转换仅限于八种基本类型与对应的包装类之间。
转义字符
\n 换行(0x0a)
\r 回车(0x0d)
\f 换页符(0x0c)
\b 退格(0x08)
\s 空格(0x20)
\t 制表符
\" 双引号
\' 单引号
\\ 反斜杠
\ddd 八进制字符(ddd)
\uxxxx 16进制Unicode字符(xxxx)
Java运算符算术运算符
算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
实例假设整数变量A的值为10,变量B的值为20:
+ 加法-相加运算符两侧的值 A+B等于30
- 减法-左操作数减去右操作数A–B等于-10
* 乘法-相乘操作符两侧的值 A*B等于200
/ 除法-左操作数除以右操作数B/A等于2
% 取模-右操作数除左操作数的余数 B%A等于0
++自增-操作数的值增加1 B++等于21
-- 自减-操作数的值减少1 B--等于19
关系运算符
方便举例子,设两个整数:变量A的值为10(二进制表示为“00001010”),变量B的值为20(二进制表示为“00010100”):
== 检查如果两个操作数的值是否相等,如果相等则条件为真。 (A==B)为假(非真)。
!= 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 (A!=B)为真。
> 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。(A>B)非真。
< 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。(A<B)为真。
>=检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 (A>=B)为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 (A<=B)为真。
位运算符
& 按位与操作符,当且仅当两个操作数的某一位都非0时候结果的该位才为1。 (A&B),得到00000000,即0
| 按位或操作符,只要两个操作数的某一位有一个非0时候结果的该位就为1。 (A|B)得到00011110,即30
^ 按位异或操作符,两个操作数的某一位不相同时候结果的该位就为1。(A^B)得到00011110,即30
〜 按位补运算符翻转操作数的每一位。 (〜A)得到11111111111111111111111111110101,即-11
<< 按位左移运算符。左操作数按位左移右操作数指定的位数。 A<<2得到00101000,即40,相当于10*4
>> 按位右移运算符。左操作数按位右移右操作数指定的位数。 A>>2得到00000010,即2,相当于10/4,余数忽略
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。
逻辑运算符
假设布尔变量A为真,变量B为假
&&称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 (A&&B)为假。
|| 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 (A||B)为真。
! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 !(A&&B)为真。
java中&和&&虽然都是逻辑运算符,都是判断两边为真,则语句成立,但是在运行的时候,还是有差别的,下面举例来说明。
publicclassTest1{publicstaticvoidmain(String[]args){inti=3;if((i++>5)&(i++<9)){System.out.println(i);System.out.println("恭喜,执行完了条件语句!");}System.out.println(i);}}
打印结果为:5
从条件判断语句来看,是不成立的,但是i是自加了两次,由初始值3变成5。
publicclassTest2{publicstaticvoidmain(String[]args){inti=3;if((i++>5)&&(i++<9)){System.out.println(i);System.out.println("恭喜,执行完了条件语句!");}System.out.println(i);}}打印结果:4
从条件判断语句来看是不成立的,变量i只自加了一次。
&和&&都是逻辑运算符,都是判断两边同时真则为真,否则为假;但是&&当第一个条件不成之后,后面的条件都不执行了,被称为“短路效应”;而&则还是继续执行,直到整个条件语句执行完为止。如&&例子中的i++>5被执行了,而i++<9并没有被执行,这就是他们的差别。&例子中的i++>5和i++<9都被执行了。
赋值运算符
= 简单的赋值运算符,将右操作数的值赋给左侧操作数 C=A+B将把A+B得到的值赋给C
+=加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 C+=A等价于C=C+A
-= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C-=A等价于C=C-
A
*=乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C*=A等价于C=C*A
/= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C/=A等价于C=C/A
(%)= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%=A等价于C=C%A
<<= 左移位赋值运算符C<<=2等价于C=C<<2
>>= 右移位赋值运算符C>>=2等价于C=C>>2
&= 按位与赋值运算符C&=2等价于C=C&2
^=按位异或赋值操作符 C^=2等价于C=C^2
|=按位或赋值操作符C|=2等价于C=C|2
条件运算符(?:)
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
variablex=(expression)?valueiftrue:valueiffalse
instanceOf运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
(Object/reference/variable)instanceOf (class/interfacetype)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
Java面向对象基础
Java继承
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承可以理解为一个对象从另一个对象获取属性的过程。
如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子类,类C是从类A继承而来的。在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类
继承中最常使用的两个关键字是extends和implements。
所有Java的类均是由java.lang.Object类继承而来的,所以Object是所有类的祖先类,而除了Object外,所有类必须有一个父类。
Java重写(Override)与重载(Overload)
重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写
重写的好处在于子类可以根据需要,定义特定于自己的行为。
也就是说子类能够根据需要实现父类的方法。
当需要在子类中调用父类的被重写方法时,要使用super关键字。
重写的原则:
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问权限不能比父类中被重写的方法的访问权限更高。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
声明为final的方法不能被重写。
声明为static的方法不能被重写,但是能够被再次声明。
如果一个方法不能被继承,那么该方法不能被重写。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
重载(overloading)是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
重载规则:
被重载的方法必须改变参数列表;
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
Java多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性是对象多种表现形式的体现。
比如我们说"宠物"这个对象,它就有很多不同的表达或实现,比如有小猫、小狗、蜥蜴等等。那么我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性。
publicinterfaceVegetarian{}publicclassAnimal{}publicclassDeerextendsAnimalimplementsVegetarian{}因为Deer类具有多重继承,所以它具有多态性。以上实例解析如下:
一个DeerIS-A(是一个)Animal
一个DeerIS-A(是一个)Vegetarian
一个DeerIS-A(是一个)Deer
一个DeerIS-A(是一个)Object
在Java中,所有的对象都具有多态性,因为任何对象都能通过IS-A测试的类型和Object类。
访问一个对象的唯一方法就是通过引用型变量。
引用型变量只能有一种类型,一旦被声明,引用型变量的类型就不能被改变了。
引用型变量不仅能够被重置为其他对象,前提是这些对象没有被声明为final。还可以引用和它类型相同的或者相兼容的对象。它可以声明为类类型或者接口类型。
当我们将引用型变量应用于Deer对象的引用时,下面的声明是合法的:
Deerd=newDeer();Animala=d;Vegetarianv=d;Objecto=d;所有的引用型变量d,a,v,o都指向堆中相同的Deer对象。
虚方法
看一个实例:
publicclassFother{publicvoidsay(Stringname){System.out.println(“Hello!”+name+”---FromFather”);};}publicclassChildextendsFother{//覆写父类的方法publicvoidsay(Stringname){System.out.println(“Hello!”+name+”---FromChild”);};}Fatherperson1=newChild();Childperson2=newChild();person1.say("Jack);person2.say("Rose");
打印出的结果为:
Hello!Jack---FromChild
Hello!Rose---FromChild
上例中,我们实例化了两个Child对象,分别以Father和Child类型来引用它。person1.say(“Jack”)表面上是调用了person1(Father类型)的方法,但是它实际上是一个虚拟方法。以Father类型引用的变量person1调用say方法时,实际上并不是调用Father类中的say方法,而是Child类中的say方法。该行为被称为虚拟方法调用。
Java封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指,一种将抽象性函式接口的实作细节部份包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
Java接口
接口(Interface),在Java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在Java中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类的区别:
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含成员变量,除了static和final变量。
接口不是被类继承了,而是要被类实现。
接口支持多重继承。
如果一个接口没有加public修饰符,该接口默认只能被同一包中的类访问,接口可以同时继承多个父接口,这与类的单一继承机制不同
接口中所有的常量都是隐式地声明为publicstaticfinal
Java抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
Number类及其子类
基本数据类型“包装”类
抽象类Number是BigDecimal、BigInteger、Byte、Double、Float、Integer、Long和Short类的超类。
Number的子类必须提供将表示的数值转换为byte、double、float、int、long和short的方法。即必须实现以下六种方法:
bytebyteValue()
以byte形式返回指定的数值。
doubledoubleValue()
以double形式返回指定的数值。
floatfloatValue()
以float形式返回指定的数值。
intintValue()
以int形式返回指定的数值。
longlongValue()
以long形式返回指定的数值。
shortshortValue()
以short形式返回指定的数值。
就是说,Number的各种子类之间是可以相互转换的,当然中间可能会涉及到相关的舍入或取整。
有一个问题需要弄清楚:Byte类是byte的包装类,Integer是int的包装类,其他以此类推。int是一种java的原始数据类型,而Integer是一种java的引用数据类型。作为实例变量的时候,未赋值的情况下,int型数据默认为0,而Integer类型的默认值为null。
Integer类在对象中包装了一个基本类型int的值。Integer类型的对象包含一个int类型的字段。
比如有下面两个变量:
inta=12;Integerb=12;//相当于Integerb=newInteger(12)针对a只能做一些加减乘除之类的运算,而b则可以使用Integer类提供的各种方法来进行操作。从本质上来讲,b是Integer的一个对象,而a不是。
Number的每个子类都提供了数字和字符串之间的转换方法,下面是Integer的相关方法,其余子类与之类似
staticIntegerdecode(Stringnm)
将String解码为Integer。
staticintparseInt(Strings)
将字符串参数作为有符号的十进制整数进行解析。
staticintparseInt(Strings,intradix)
使用第二个参数指定的基数(二进制,八进制,十六进制,甚至三进制、二十进制都可以),将字符串参数解析为有符号的整数。
staticStringtoString(inti)
返回一个表示指定整数的String对象。
staticStringtoString(inti,intradix)
返回用第二个参数指定基数表示的第一个参数的字符串表示形式。
staticIntegervalueOf(inti)
返回一个表示指定的int值的Integer实例。
staticIntegervalueOf(Strings)
返回保存指定的String的值的Integer对象。
staticIntegervalueOf(Strings,intradix)
返回一个Integer对象,该对象中保存了用第二个参数提供的基数进行解析时从指定的String中提取的值。
staticStringtoBinaryString(inti)
以二进制(基数2)无符号整数形式返回一个整数参数的字符串表示形式。
staticStringtoHexString(inti)
以十六进制(基数16)无符号整数形式返回一个整数参数的字符串表示形式。
staticStringtoOctalString(inti)
以八进制(基数8)无符号整数形式返回一个整数参数的字符串表示形式。
Integer类中还有四个静态变量,其余子类与之类似
staticintMAX_VALUE
表示int类型能够表示的最大值。
staticintMIN_VALUE
表示int类型能够表示的最小值。
staticintSIZE
用来以二进制补码形式表示int值的比特位数。返回32
staticClass<Integer>TYPE
表示基本类型int的Class实例。返回int
在Number的子类中都含有一对方法,用于将字符串转换为数字,是parseXXXX()与valueOf()。但是两者是有区别的。例如:
Stringnum=“123”;Integera=Integer.valueOf(num);//返回的是一个Integer对象intb=Integer.parseInt(num);//返回的是一个int型数据
BigInteger类
不可变的任意精度的整数。所有操作中,都以二进制补码形式表示BigInteger(如Java的基本整数类型)。BigInteger提供所有Java的基本整数操作(加减乘除等基本运算),并提供java.lang.Math的所有相关方法。另外,BigInteger还提供以下运算:模算术、GCD计算、质数测试、素数生成、位操作以及一些其他操作。
内置常量:
staticBigIntegerONE
BigInteger的常量1。
staticBigIntegerTEN
BigInteger的常量10。
staticBigIntegerZERO
BigInteger的常量0。
构造方法:
BigInteger(Stringval)
将BigInteger的十进制字符串表示形式转换为BigInteger。
BigInteger(Stringval,intradix)
将指定基数的BigInteger的字符串表示形式转换为BigInteger。
方法摘要:
BigIntegerabs()
返回其值是此BigInteger的绝对值的BigInteger。
BigIntegeradd(BigIntegerval)
返回其值为(this+val)的BigInteger。
BigIntegerdivide(BigIntegerval)
返回其值为(this/val)的BigInteger。
BigInteger[]divideAndRemainder(BigIntegerval)
返回包含(this/val)后跟(this%val)的两个BigInteger的数组。
BigIntegergcd(BigIntegerval)
返回一个BigInteger,其值是abs(this)和abs(val)的最大公约数。
booleanisProbablePrime(intcertainty)
如果此BigInteger可能为素数,则返回true,如果它一定为合数,则返回false。
BigIntegermax(BigIntegerval)
返回此BigInteger和val的最大值。
BigIntegermin(BigIntegerval)
返回此BigInteger和val的最小值。
BigIntegermod(BigIntegerm)
返回其值为(thismodm)的BigInteger。
BigIntegermodInverse(BigIntegerm)
返回其值为(this-1modm)的BigInteger。
BigIntegermodPow(BigIntegerexponent,BigIntegerm)
返回其值为(thisexponentmodm)的BigInteger。
BigIntegermultiply(BigIntegerval)
返回其值为(this*val)的BigInteger。
BigIntegernegate()
返回其值是(-this)的BigInteger。
BigIntegernextProbablePrime()
返回大于此BigInteger的可能为素数的第一个整数。
BigIntegerpow(intexponent)
返回其值为(thisexponent)的BigInteger。
BigIntegerremainder(BigIntegerval)
返回其值为(this%val)的BigInteger。
BigIntegersubtract(BigIntegerval)
返回其值为(this-val)的BigInteger。
在这里需要对取余和取模做一下区分,求模运算与求余运算不同
取余运算时,得到的结果符号永远与被除数相同,跟除数的符号无关。例如:
BigIntegera1=newBigInteger("16");BigIntegerb1=newBigInteger("12");BigIntegera2=newBigInteger("16");BigIntegerb2=newBigInteger("-12");System.out.println(a1.remainder((b1)));//结果为4System.out.println(a2.remainder(b2));//结果为4
取模时,被除数必须为正数,结果恒为正
BigIntegera1=newBigInteger("-16");BigIntegerb1=newBigInteger("12");BigIntegera2=newBigInteger("16");BigIntegerb2=newBigInteger("12");System.out.println(a1.mod((b1)));//结果为8(-12*2+8=-16)System.out.println(a2.mod(b2));//结果为4(12+4=16)
BigDecimal类
不可变的、任意精度的有符号十进制数。BigDecimal由任意精度的整数非标度值和32位的整数标度(scale)组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以10的负scale次幂。因此,BigDecimal表示的数值是(unscaledValue×10-scale)。
BigDecimal类使用户能完全控制舍入行为。如果未指定舍入模式,并且无法表示准确结果,则抛出一个异常;否则,通过向该操作提供适当的MathContext对象,可以对已选择的精度和舍入模式执行计算。
构造方法摘要:
BigDecimal(BigIntegerval)
将BigInteger转换为BigDecimal。
BigDecimal(BigIntegerunscaledVal,intscale)
将BigInteger非标度值和int标度转换为BigDecimal。
BigDecimal(char[]in)
将BigDecimal的字符数组表示形式转换为BigDecimal
BigDecimal(doubleval)
将double转换为BigDecimal,后者是double的二进制浮点值准确的十进制表示形式。
BigDecimal(intval)
将int转换为BigDecimal。
BigDecimal(longval)
将long转换为BigDecimal。
BigDecimal(Stringval)
将BigDecimal的字符串表示形式转换为BigDecimal。
可见,BigDecimal类可以接受int、long、double、String以及BigInteger类型的数据来构造实例。
一般来说,16位有效数字以下的运算使用Double类型就足够了,如果需要16位有效数字以上的,应该使用BigDecimal
将BigDecimal转换为其他类型的数据时,就会涉及到舍入的问题,该类提供了8中舍入模式。
字段摘要:
staticBigDecimalONE
值为1,标度为0。
staticintROUND_CEILING
接近正无穷大的舍入模式。
staticintROUND_DOWN
接近零的舍入模式。
staticintROUND_FLOOR
接近负无穷大的舍入模式。
staticintROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
staticintROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
staticintROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
staticintROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。
staticintROUND_UP
舍入远离零的舍入模式。
staticBigDecimalTEN
值为10,标度为0。
staticBigDecimalZERO
值为0,标度为0。
doubled=3.1415926;//接近正无穷大的舍入模式,5位精度,输出3.14160System.out.println(newBigDecimal(d).setScale(5,BigDecimal.ROUND_CEILING));//接近负无穷大的舍入模式,5位精度,输出3.14159System.out.println(newBigDecimal(d).setScale(5,BigDecimal.ROUND_FLOOR));//接近零的舍入模式,5位精度,输出3.14159System.out.println(newBigDecimal(d).setScale(5,BigDecimal.ROUND_DOWN));//远离零的舍入模式,5位精度,输出3.14160System.out.println(newBigDecimal(d).setScale(5,BigDecimal.ROUND_UP));//四舍五入的舍入模式,5位精度,输出3.14159System.out.println(newBigDecimal(d).setScale(5,BigDecimal.ROUND_HALF_UP));
除了使用setScale()函数设置取舍模式,还可以在构造函数中传入一个MathContext对象来设置。如
doubled=3.1415926;//四舍五入的舍入模式,5位有效数字MathContextcontext=newMathContext(5,RoundingMode.HALF_UP);BigDecimaldec=newBigDecimal(d,context);System.out.println(dec);//输出3.1416
方法摘要:
BigDecimalabs()
返回BigDecimal,其值为此BigDecimal的绝对值,其标度为this.scale()。
BigDecimaladd(BigDecimalaugend)
返回一个BigDecimal,其值为(this+augend),其标度为max(this.scale(),augend.scale())。
BigDecimaldivide(BigDecimaldivisor)
返回一个BigDecimal,其值为(this/divisor),其首选标度为(this.scale()-divisor.scale());如果无法表示准确的商值(因为它有无穷的十进制扩展),则抛出ArithmeticException。
BigDecimaldivide(BigDecimaldivisor,intscale,introundingMode)
返回一个BigDecimal,其值为(this/divisor),其标度为指定标度。
BigDecimal[]divideAndRemainder(BigDecimaldivisor)
返回由两个元素组成的BigDecimal数组,该数组包含divideToIntegralValue的结果,后跟对两个操作数计算所得到的remainder。
BigDecimaldivideToIntegralValue(BigDecimaldivisor)
返回BigDecimal,其值为向下舍入所得商值(this/divisor)的整数部分。
BigDecimalmax(BigDecimalval)
返回此BigDecimal和val的最大值。
BigDecimalmin(BigDecimalval)
返回此BigDecimal和val的最小值。
BigDecimalmultiply(BigDecimalmultiplicand)
返回一个BigDecimal,其值为(this×multiplicand),其标度为(this.scale()+multiplicand.scale())。
BigDecimalnegate()
返回BigDecimal,其值为(-this),其标度为this.scale()。
BigDecimalplus()
返回BigDecimal,其值为(+this),其标度为this.scale()。
BigDecimalpow(intn)
返回其值为(thisn)的BigDecimal,准确计算该幂,使其具有无限精度。
intprecision()
返回此BigDecimal的精度。
BigDecimalremainder(BigDecimaldivisor)
返回其值为(this%divisor)的BigDecimal。
intscale()
返回此BigDecimal的标度。
BigDecimalscaleByPowerOfTen(intn)
返回其数值等于(this*10n)的BigDecimal。
BigDecimalsetScale(intnewScale)
返回一个BigDecimal,其标度为指定值,其值在数值上等于此BigDecimal的值。
BigDecimalsetScale(intnewScale,introundingMode)
返回一个BigDecimal,其标度为指定值,其非标度值通过此BigDecimal的非标度值乘以或除以十的适当次幂来确定,以维护其总值。
BigDecimalsetScale(intnewScale,RoundingModeroundingMode)
返回BigDecimal,其标度为指定值,其非标度值通过此BigDecimal的非标度值乘以或除以十的适当次幂来确定,以维护其总值。
BigDecimalstripTrailingZeros()
返回数值上等于此小数,但从该表示形式移除所有尾部零的BigDecimal。
BigDecimalsubtract(BigDecimalsubtrahend)
返回一个BigDecimal,其值为(this-subtrahend),其标度为max(this.scale(),subtrahend.scale())。
java.text.DecimalFormat类可以控制浮点数的输出格式,前缀零、后缀零以及千位分隔符等
importjava.text.*;publicclassDecimalFormatDemo{staticpublicvoidcustomFormat(Stringpattern,doublevalue){DecimalFormatmyFormatter=newDecimalFormat(pattern);Stringoutput=myFormatter.format(value);System.out.println(value+""+pattern+""+output);}staticpublicvoidmain(String[]args){customFormat("###,###.###",123456.789);customFormat("###.##",123456.789);customFormat("000000.000",123.78);customFormat("$###,###.###",12345.67);}}输出:
123456.789 ###,###.### 123,456.789
123456.789 ###.## 123456.79
123.78 000000.000 000123.780
12345.67 $###,###.### $12,345.67
字符串
String是不可变的对象,它们的值在创建之后不能更改。JDK中有很多方法看似改变了String,实际上是重新创建了一个包含修改结果的新的对象。
String对象中有一个常用方法substring(intbeginIndex,intendIndex),该方法可以截取指定起始下标的子串。需要注意的是,指定的起始下标是“左闭右开”区间,包括JDK中的其他方法,如操作数组,集合等,都遵循这一原则。
例如:
StringanotherPalindrome="Niagara.Oroaragain!";
Stringroar=anotherPalindrome.substring(11,15);
substring(11,15)得到的是下标为11、12、13、14字符组成的子串,不包括下标为15的字符。