1、JAVA CLASSPATH
CLASSPATH一般设置为.;%JAVA_HOME%/lib;这样JAVA解释器首先在当前目录寻找类,如果未找到则到lib目录寻找。
这样如果当前目录
如果类如Good.java在E:下,这里在CMD中切换到E:编译及解释都通过,但切换到D:则会提示找不到类Good.这时可在CMD中输入命令SET CLASSPATH=E:
这样JAVA解释器执行时会从CLASSPATH所定的目录开始寻找,这时会发现即使命令提示符在D:输入JAVA Good仍可执行。
按此思路如果此时我输入命令:SET CLASSPATH=D:/,而Good.java文件仍在E:根目录下,此时我在CMD中切换到E:,执行JAVA Good会发现找不到类Good.
这还是因为寻找的路径是按SET CLASSPATH来的,所以在这里你会发现虽然明明E:下有这个文件,但切换到这个目录反而执行不了。。
要想解决这个问题,需要修改CLASSPATH,如SET CLASSPATH=D:/;.;这样如果在D下面寻找不到所需的类,会再到当前目录寻找。这里要注意的是这个"当前"是指的CMD中你所切换的目录,如果此时不在E:而在F:下执行命令,也会提示找不到类的。
2、JAVA数据类型
JAVA数据类型分数值数据类型和布尔数据类型(boolean),数值数据类型分字符类型char,整型(short,int,long),实型(float,double)型。
boolean和byte为1个字节,char,short为两个字节,int,float为四个字节,long,double为八个字节。
关于基本数据类型运算时转换时的几点说明:
1、运算时总是将小的类型转换为运算式中最大的类型再进行运算;
2、byte,char,short不会互相转换,它们会先转换成int类型再运算;
3、整型默认为int类型,实型默认为double类型。这条很重要,很多下面的问题都可从这里得到解释。如
(1) 注意:byte类型参与运算的时候会自动转为int型。。
byte b=2; //特殊点:JAVA中允许将int类型直接声明给byte,char,short类型,即byte b=2; 中2为int类型;char c=12;但要注意不能超过它们各自的范围。如byte最大为128则不能byte b=129;
b=b*3; //这里运算时,将b看作int类型,3为int类型。所以要转换。
这里应使用强制类型转换:b=(byte)(b*3);
但是注意了:
long L1=123; //没错
long L1=L1+23; //没错
(2) 实型:
float f=3.2;
这个定义编译时会出错。原因是系统会将3.2当作double型来看,所以这里改为:
float f=3.2f;
(3)注意各类型变量的默认值,boolean型默认为true,值只有true和false.
(4)数组:不能象c语言一样定义:int a[3];
JAVA中定义数组应为:int[] a = new int[3];
也要注意:int[] num={1,2,3};这是对的,但下面却会提示错误。
int[] num1;
num1={1,2,3,4};//error
还要注意:int[] num1=new int[]{1,2,3};//right
int[] num2=new int[3]{1,2,3};//error,如果在后面已列出值就不要再加3这一数据元素个数了。
另一种数组的定义和赋值方式:
int[] num=new int[3];
num[0]=2;num[1]=4;num[2]=322;
(5)字符的赋值:可以给字符赋整数如:char c=97;
3、for语句
for(int i=0;i<10;i++)
{
System.out.println("sf");
}
System.out.println(i);//error*/
要注意,java是for语句中定义的i,只在for语句范围内有效。
4、位运算
位运算有&(按位与)、|(按位或)、^(按位异或)、~取反,<<左移运算符,>>右移运算符。
按位与:
一般用于清零,即找一个与其相反的数相与结果为0.如:00101011与10010100按位与结果为00000000
按位与另一用途为保留整数的高位或低位字节.如将:00101100 10101100与0000000011111111或1111111100000000相按位与操作结果使其只保留低位字节和高位字节.
按位或:
常用于将低八位或高八位全置为1.如a:00101100 10101100 与o(0377)即二进制的00000000 11111111相或,置低八位为1.
按位异或:
按位异或,即同则0异则1.如a:00111001^00101100=00010101
常用于使特定位翻转,即想使哪几位由1变为0,只需将其与1异或即可.
与0相异或,保留原值.
异或还用做交换两个值,不用临时变量...
fn(int x,int y)
{
x=x+y;
y=x-y;
x=x-y;
}
如a=3,b=4想将a,b的值互换只需:a=a^b,b=b^a,a=a^b;
如: a=011
(^)b=100
_____________
a=a^b=111=7 (异或之后:a=7)
(^)b=100
_____________
b=b^a=011=3 (异或之后:b=3)
a=111=7
______________
a=a^b=100=4 (异或之后:a=4)
最终完成了a与b的互换.这可用于排序中交换值,而不用声明temp变量.
推导:b=b^a,而a=a^b,有b=b^(a^b)=a^b^b,b^b=0,所以:b=a^0,而上面说过与0异或保留值.故b=a;
a=a^b,而a=a^b,b=b^a=b^(a^b),所以,a=a^b^(b^(a^b))=a^a^b^b^b=b;即a=b;
按位取反:各位均取反。
左移运算符:二进制位整体向左移,右补0,如:3=00000011,向左移两位,右补0得:00001100=12,由此可知,左移相当于乘2,左移两位3*4=12;
左移运算比乘法快很多,有些C编译程序自动将乘2的运算用左移一位来实现,将乘2(n)的幂运算处理为左移n位。
但要注意的是:如c语言中unsign int 为2字节,取值范围为0-255,所以64(01000000)左移一位时溢出为0,乘2,128,如果再左移一位,则溢出1,得到结果为0.
右移运算符:右移n位相当于除以2的n次方。
右移需要注意符号位的问题,如果符号位为0,即为正,则移入0.如果符号位为1即负数,则右移之后,左边是补0还是补1取决于所用的计算机系统。
左边补0的称为逻辑右移,即简单右移;
左边补1的称为算术右移。
上述为c语言中的右移表述,
在java及javascript语法中
注意>>(Signed right shift)表示带符号右移,即右移后左补1;
>>>(Unsigned right shift)表示无符号右移,即右移后左补0;
如: a x x<<2 x>>2 x>>>2
17 00010001 00|01000100 00000100|01 00000100|01
-17 11101111 (补码) 11|10111100 11111011|11(补码) 00111011|11
如:a(113755) 1001011111101101 a>>1:0100101111110110(逻辑左移),a>>1:1100101111110110(算术右移)
用操作系统上的计算机由10进制到二进制的转换得到的就是补码值.
5、求一负数的补码。(正数的补码与原码是一样的,所以如果给出补码符号位为0则表示正数,补码即原码)
如题:(1)将其转换成十进制数,取它的绝对值(2)将它的绝对值的二进制行式取反(3)取反后加1、
如-6.求6的二进制为:00000110,取反得:11111001,加1:11111010,-6的补码即为:11111010.
上面方法也可以理解为:负数的符号位不变,其它各位取反再加1.即-6:10000110, 取反:11111001,加1:11111010.
6、已知一个负数的补码,求这个负数的值的步骤。
如题:(1)将各位取反。(2)将其转换成十进制数(3)加上负号再减1.
如:x的补码为:11111010,求x=? 采用以上方法得:取反:00000101,得5,加上负号再减1,-5-1=-6.
x=-6.
如上面例4中-17经过右移后得:11111011求这个值。取反:00000100,十进制4,加负号再减1得-5.
(2)也可以先依靠补码求出原码,再求值.方法就是对该补码再求补码
如:x的补码为:11111010,求x的原码.由于最高位为1即负数,所以符号位不变其余各位取反得10000101,再加1得10000110即-6
7、在JAVA中BIN目录中JAVAP为反编译工具,通过JAVAP Student可以查看到Student.java中的有关成员变量和方法的定义。
8、构造函数:当你自己新创建了一构造函数时即进行了构造函数的重载时,还应该声明默认的构造函数,因为当你创建了新的构造函数时系统不会自动创建默认构造函数
所以当你要创建一个不带参数的对象时,编译器会提示没有相应的构造函数与之对应。但当你创建对象是传了参数则不会提示出错。
9、this关键字的使用:
(1)、当成员变量中的变量名与成员函数也包括构造函数中的形参名相同时,可在函数内使用this.x=x来引用成员数据并为成员变量赋值。
(2)、需明确使用当前对象的句柄,this返回当前对象的引用。
例如:public class Leaf {
private int i = 0;
Leaf increment() {
i++;
return this;
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increment().increment().increment().print();
}
}
(3)、在构建器里调用构建器:注意的是应该放在构造函数的最前面。
例如:public class Flower {
private int petalCount = 0;
private String s = new String("null");
Flower(int petals) {
petalCount = petals;
System.out.println(
"Constructor w/ int arg only, petalCount= "
+ petalCount);
}
Flower(String ss) {
System.out.println(
"Constructor w/ String arg only, s=" + ss);
s = ss;
}
Flower(String s, int petals) {
this(petals);
//! this(s); // Can't call two!
this.s = s; // Another use of "this"
System.out.println("String & int args");
}
Flower() {
this("hi", 47);
System.out.println(
"default constructor (no args)");
}
void print() {
//! this(11); // Not inside non-constructor!
System.out.println(
"petalCount = " + petalCount + " s = "+ s);
}
public static void main(String[] args) {
Flower x = new Flower();
x.print();
}
}
尽管可用this调用一个构建器,但不可调用两个
10、static关键字的使用:
Static关键字:
通常只有创建了对象才能分配内存,才能够使用类中的方法.假如我要一段代码保存数据的内存或我要一个不从属任何对象的方法,则通过Static关键字实现.
当你声明某种东西是static的时候,你的意思是这项数据或方法没有被连到任何一个类的实例.因此即使不创建这个类 的实例也可以使用这个static数据或方法.
但是static的方法不能直接访问非static的成员或方法.
static数据及方法的访问:
class StaticTest
{
static int i=23;
}
(1)可以用两种方法使用变量i,
一种使用类的对象StaticTest st1=new StaticTest();st1.i++;
一种是直接使用类名.StaticTest.i++;
注意:如果使用类的多个对象使用变量i时,改变其中一个另一个跟着改变,因为他们使用的是同一段内存.
StaticTest st1=new StaticTest();
StaticTest st2=new StaticTest();
st1.i++;
st2.i++;
(2)特别注意:
public class Point
{
int x;
void output()
{}
static void output(int x)
{
this.x=x;//这里static声明的成员函数是不能访问非static的成员变量的。除非将x声明成:static int x;
}
}
(3)将static变量也称为类变量,其他变量则可称为实例变量。同样有类方法,实例方法。
要注意的是:在非static方法中是可以调用static方法和成员变量的。
如main函数中的方法.
public static void main(String[] args){
System.out.println("slfsfsfsf");
}
因为out是System类中声明为static类型的对象
另外main方法也是定义成static的,这是因为当类加载时并没有创建实例,而JAVA中都是以类为单元的,所以这里定义为static,便于不同类加载它。
11、final
final int a=3;
或final int a;
Point()
{
//this(1,1);注意:this(1,1)一定要放在构造函数最前面。
a=3;
}
Point(int x,int y)
{
a=3; //注意:如果final类型的常量在定义时没有初始化,那么在构造函数中初始化时需要注意,即所有构造函数中都要有初始化语句a=3;
}
即final声明一个常量并初始化,或先声明然后在构造函数中初始化。
但是要注意的是:
常量一般声明为static,即static final int a=3;这里因为声明的是static的不属于任何实例的,所以这时一定要在常量声明时初始化,不能在构造函数中初始化了。
12、继承性
JAVA不允许类的多继承。C++当中是允许多继承的。当子类与超类中存在相同的方法时,超类的方法被隐藏,即超类对象调用超类中的方法,子类对象调用子类的方法。
如:class Animal
{
int height ,weight;
void sleep()
{
System.out.println(" Animal sleep");
}
void eat()
{
System.out.println(" Animal eate");
}
}
class Fish extends Animal
{
void eat()
{
System.out.println(" Fish eate");
}
}
class Integeration
{
public static void main(String[] args)
{
Animal an= new Animal();
Fish fh = new Fish();
an.eat();
fh.eat();
}
}
结果为: Animal eate
Fish eate
13、super的使用
super用于在子类中对父类的访问
可以利用super来访问父类被子类隐藏的变量或方法。也就是说当父类与子类都有某一方法的定义时,如果我想在子类中调用父类中的这个方法,那么使用super来实现。
如:class Cleanser {
private String s = new String("Cleanser");
public void scrub() { append(" scrub()"); }
public void print() { System.out.println(s); }
}
public class Detergent extends Cleanser {
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
public static void main(String[] args) {
Detergent x = new Detergent();
x.scrub();
x.print();
}
}
14、继承的执行顺序及构造函数的调用过程:
程序如下:
class act1
{
act1(){
System.out.println("act1 constructor");}
}
class act2 extends act1
{
act2(){
System.out.println("act2 constructor");}
}
public class act3 extends act2
{
act3(){
System.out.println("act3 constructor");}
public static void main(String[] args)
{
act3 x=new act3();
}
}
结果如下:
C:/java>java act3
act1 constructor
act2 constructor
act3 constructor
说明:在衍生类的构建器中,Java会自动插入对基础类构建器的调用。这里正是super关键字的用处所在:即在子类中隐藏了语句super。父类构造函数名()。
15、以下示例用带参数的构造函数来说明super在继承中调用构造函数时所起的作用。
class act11
{
act11(int i){
System.out.println("act11 constructor");}
}
class act12 extends act11
{
act12(int i){
super(i);
System.out.println("act12 constructor");}
}
public class act13 extends act12
{
act13(){
super(13);
System.out.println("act13 constructor");}
public static void main(String[] args)
{
act13 x=new act13();
}
}
结果为:
C:/java>java act13
act11 constructor
act12 constructor
act13 constructor
说明:
含有自变量的构建器
例14中有自己默认的构建器;也就是说,它们不含任何自变量。编译器可以很容易地调用它们,因为不存在具体传递什么自变量的问题。
如果类没有默认的自变量,或者想调用含有一个自变量的某个基础类构建器,必须明确地编写对基础类的调用代码。这是用super关键字以及适当的自变量列表实现的。
注意区别super与this的使用,this用于在同一类中某一构造函数中调用另一构造函数;而super则不只限于构造函数,用于在继承类中调用父类中的具有相同特性的函数。
super用来调用基类的带参数的构造函数时应该注意的地方:
看下面两段代码:一个是在继承类中有super(i)调用基类带参数构造函数,另一个则没有,注意看他们的输出值。
public class Test {
public static int i ;
public static void main(String[] args) {
TT t = new TT(23);
}
}
class T{
protected float j=2.0f;
public T(){
System.out.println("fater class constuctor");
}
public T(int i){
System.out.println("fater class constuctor tt="+i);
}
}
class TT extends T{
public TT(){
System.out.println("child class contructor");
}
public TT(int i){
super(i);
System.out.print("chiled="+i);
}
}
输出结果为:
fater class constuctor tt=23
chiled=23
再看:
public class Test {
public static int i ;
public static void main(String[] args) {
TT t = new TT(23);
}
}
class T{
protected float j=2.0f;
public T(){
System.out.println("fater class constuctor"); //当没用用super(i)显式调用基类的构造方法的时候,JAVA虚拟机会自动找基类的无参构造函数。
}
public T(int i){
System.out.println("fater class constuctor tt="+i);
}
}
class TT extends T{
public TT(){
System.out.println("child class contructor");
}
public TT(int i){
//super(i); //作了注释
System.out.print("chiled="+i);
}
}
输出结果为:
fater class constuctor
chiled=23
无super(i)调用时,自动调用基类无参的构造方法,再执行自身的构造方法
下面我把基类的无参构造方法注释掉:
public class Test {
public static int i ;
public static void main(String[] args) {
TT t = new TT(23);
}
}
class T{
protected float j=2.0f;
/*
public T(){
System.out.println("fater class constuctor");
}
*/
public T(int i){
System.out.println("fater class constuctor tt="+i);
}
}
class TT extends T{
public TT(){
System.out.println("child class contructor");
}
public TT(int i){
//super(i);
System.out.print("chiled="+i);
}
}
提示报错:
Test.java:19: 找不到符号
符号: 构造函数 T()
位置: 类 T
public TT(){
16、java中文件名及public类名关系
(1) 每个编译单元(文件)都只能有一个public类。每个编译单元有一个公共接口的概念是由那个公共类表达出来的。根据自己的需要,它可拥有任意多个提供支撑的“友好”类。但若在一个编译单元里使用了多个public类,编译器就会向我们提示一条出错消息。
(2) public类的名字必须与包含了编译单元的那个文件的名字完全相符,甚至包括它的大小写形式。所以对于Widget来说,文件的名字必须是Widget.java,而不应是widget.java或者WIDGET.java。同样地,如果出现不符,就会报告一个编译期错误。
(3) 可能(但并常见)有一个编译单元根本没有任何公共类。此时,可按自己的意愿任意指定文件名,不过应注意,在通过java命令执行的时候,后面应该跟:具有main方法的类名。
17、多态性
动态绑定:指执行期间(而非编译期间)判断所引用对象的实际类型,根据其实际的类型调用相应方法。
首先:多态性是通过覆盖父类的方法来实现的,在运行时通过传递的对象引用来调用相应的方法。多态性与方法的重载和覆盖是密不可分的。
class Animal
{
int height ,weight;
void sleep()
{
System.out.println(" Animal sleep");
}
void eat()
{
System.out.println(" Animal eate");
}
}
class Fish extends Animal
{
void eat()
{
System.out.println(" Fish eate");
}
}
class Integeration
{
static void fn(Animal an)
{
an.eat();
}
public static void main(String[] args)
{
Animal an;
Fish fh=new Fish();
an=fh;
Integeration.fn(an);
}
}
结果为:Fish eate.
类中通过eat方法的覆盖,及将Fish引用赋给Animal引用,当调用静态方法fn时,虽然参数类型为Animal,但实际传过来的是Fish的引用,调用的是子类Fish类中的eate方法;
java利用其多态性输出结果为:Fish eate.
在这里注意,假如Fish类中没有eate方法,那么结果将是输出Animal eate.
java中:重载也称为编译时多态,覆盖称为运行时多态。
>>> 对象的转型
规则:
1、一个基类的引用可以指向其子类的对象。 如:Animal a = new Dog();
2、一个基类的引用不可访问子类新增的成员变量或方法。
派生类新增的功能基类肯定看不到。。
3、可使用(引用变量 instanceof 类名)来判断该引用变量所"指向"的对象是否属于该类或该类的子类。
4、子类的对象可以当作基类来使用称向上转型upcasting,反之称为向下转型downcasting.
如下:Animal类,Dog类为子类
public class TestIns{
public static void main(String[] args){
Dog d = new Dog("xiaohei","black");
d.print();
d.swim();
Animal a = new Dog("xiaobai","white");
a.print();
// a.swim(); //Animal a只认Dog类中从基类中继承出来的方法或成员,对swim()这一子类中新增的方法不可见。
}
}
class Animal{
String name;
public Animal(String name){
this.name = name;
}
public void print(){
System.out.println("name="+name);
}
}
class Dog extends Animal{
String furColor;
public Dog(String name,String furColor){
super(name);
this.furColor = furColor;
}
public void swim(){
System.out.println("Dog swimming");
}
public void print(){
System.out.println("name="+name+", furColor="+furColor);
}
}
输出结果:
name=xiaohei, furColor=black
Dog swimming
name=xiaobai, furColor=white
父类引用指向子类对象
public class TestIns{
public static void main(String[] args){
Dog d = new Dog("xiaohei","black");
d.print();
d.swim();
System.out.println(a.furColor);
Animal a = new Dog("xiaobai","white"); //这里使用父类引用指向子类对象,但此时引用只把该对象当作普通的animal,即看不到dog的其他特性。
a.print();
//System.out.println(a.furColor); //a无法访问子类的新成员。
// a.swim();
Dog d1 = (Dog)a;
d1.swim();
System.out.println(d instanceof Dog);
System.out.println(d instanceof Animal);
System.out.println(a instanceof Dog);
System.out.println(a instanceof Animal);
}
}
class Animal{
String name;
public Animal(String name){
this.name = name;
}
public void print(){
System.out.println("name="+name);
}
}
class Dog extends Animal{
String furColor;
public Dog(String name,String furColor){
super(name);
this.furColor = furColor;
}
public void swim(){
System.out.println("Dog swimming");
}
public void print(){
System.out.println("name="+name+", furColor="+furColor);
}
}
可以在方法的参数里面使用父类的引用,但传入一个子类的对象。这样不管多少个子类对象都可以得到执行。
>>> 再看下例:
class Animal{
String name;
Animal(String name){
this.name=name;
}
public void enjoy(){
System.out.println("动物叫。。。。");
}
}
class Cat extends Animal{
String furColor;
Cat(String name,String furColor){
super(name);
this.furColor = furColor;
}
public void enjoy(){
System.out.println("猫叫。。。。。");
}
}
class Dog extends Animal{
String DurColor;
Dog(String name,String DurColor){
super(name);
this.DurColor = DurColor;
}
public void enjoy(){
System.out.println("狗叫。。。。");
}
}
class Laddy{
String name;
Animal pet;
Laddy(String name,Animal pet){
this.name = name ;
this.pet = pet;
}
public void mypetEnjoy(){
pet.enjoy();
}
}
public class TestDuotai{
public static void main(String[] args){
Cat c = new Cat("cat","blue");
Dog d = new Dog("dog","black");
Laddy l1= new Laddy("limei",c);
Laddy l2 = new Laddy("zhanli",d);
l1.mypetEnjoy();
l2.mypetEnjoy();
}
}
输出结果为:
猫叫。。。。。
狗叫。。。。
由上例子可以看出实现多态要有三个条件:
要有继承
要有重写
要有父类引用指向子类对象。
>>> 接口引用指向对象的时候也发生了多态。如下:
interface Singer{
public void sing();
public void sleep();
}
class Student implements Singer{
public void sing(){
System.out.println("student singer");
}
public void sleep(){
System.out.println("student sleep");
}
public void study(){
System.out.println("student study");
}
}
public class TestInterface{
public static void main(String[] args){
Student s = new Student();
s.sing();
s.sleep();
s.study();
Singer s1 = new Student();
s1.sing();
s1.sleep();
//s1.study(); 跟类的继承时父类的引用指向子类对象一样,看不到子类中的新的方法。
//这里把s1只当作Singer只看得到sing()和sleep();
}
}
18、instanceof判断引用是否为类的实例。
class Animal
{
int height ,weight;
void sleep()
{
System.out.println(" Animal sleep");
}
void eat()
{
System.out.println(" Animal eate");
}
}
class Fish extends Animal
{
}
class Integeration
{
static void fn(Animal an)
{
an.eat();
}
public static void main(String[] args)
{
Animal an=new Animal();
Fish fh=new Fish();
//an=fh; //(1)
if(fh instanceof Animal)
System.out.println("fh is instance of animal");
else
System.out.println("fh is not instance of animal");
if(an instanceof Fish)
System.out.println("an is instance of fish");
else
System.out.println("an is not instance of fish");
}
}
结果为:
fh is instance of animal
an is not instance of fish
取消注释(1)得结果:
fh is instance of animal
an is instance of fish
19、包
源码:
package com.mybook;
public class Book
{
public static void main(String[] args)
{
System.out.println("java package");
}
}
JAVA的包与文件系统中的目录结构要求对应:
那么也就是说com.mybook我要在对应的目录中创建com和mybook文件夹。可利用命令javac -d . Book.java,表示在Book.java所在目录进行编译,同时将编译的结果,即生成的Book.class文件置于com文件夹下的mybook文件夹下。
当然也可以将class文件生成在其它目录中:javac -d d:/ Book.java,即将在d:/com/mybook下生成Book.class文件。
20、类、变量、方法的修饰符
(1) 类的修饰符:
表示类的访问权限(public,private,default package 注意:类没有protected修饰符)和一些其他特性(abstract,final等)。
如果类没有声明任何表访问权限的修饰符则为默认包权限。即同一包中的类可以互相访问。如果从另一包中访问这个包中的类,则此类需声明为public的。如果类声明为private的,则只有类的创建者才有权限使用,
我们一般不这样声明,如果想别的类不能访问此类,大可将此类的构造函数声明为private的。
final类:final类表示此类为最终类,即此类不能被派生或覆盖。如java.lang.String类即为final类。
如:编写类Ex1.java
import java.lang.*;
public class Ex1 extends String
{
public static void main(String[] args)
{
System.out.println("sf");
}
}
运行结果为:
Ex1.java:2: 无法从最终 java.lang.String 进行继承
public class Ex1 extends String
1 错误
(2) 方法的修饰符
方法声明格式为:[<修饰符>]<返回类型><方法名>([<参数>])[throws <异常类>]{}
如:public static final int Book(int x,int y) throws IOException{...}
访问权限(public,protected,private,default),方法的其它修饰符(static, final,abstract,native,synchronized)
图示访问权限:
public protected default private
同类 Y Y Y Y
同包 Y Y Y N
子类 Y Y N N
不同包非继承 Y N N N
关于方法的访问权限修饰符21中仍有说明。
方法的其它修饰符:
1、static关注第10个学习点。
2、final方法:
final方法是为确保方法在继承的过程中保持不变,即不能被子类改变(覆盖)。
类中所有private和static方法自然成为final方法。
3、抽象方法与抽象类:
(1)在类中没有方法体的方法即为抽象方法,含有抽象方法的类即为抽象类;
package com.my;
public abstract class My //有抽象方法的类为抽象类
{
public abstract void fn();//抽象方法;
public static void main(String[] args)
{
System.out.println("my ");
}
}
(2)如果一个子类没有实现抽象基类中的任何抽象方法,则子类也为抽象类。
父类:
package com.my;
public abstract class My
{
public abstract void fn();
public static void main(String[] args)
{
System.out.println("my ");
}
}
子类:
package com.my;
public class Msun extends My
{
public static void main(String[] args)
{
System.out.println("msun ");
}
}
编译:javac -d . My.java
javac -d . Msun.java
java com.my.My
java com.my.Msun //出错提示:msun.java:2: com.my.Msun 不是抽象的,并且未覆盖 com.my.My 中的抽象方法 fn()
即:如果父类为抽象类,则子类要么声明为抽象类,要么覆盖父类的抽象方法。
修改子类如下:
子类:
package com.my;
public abstract class Msun extends My
{
public static void main(String[] args)
{
System.out.println("msun ");
}
}
或
package com.my;
public class Msun extends My
{
public void fn()
{System.out.println("overrite ");}
public static void main(String[] args)
{ Msun ms = new Msun();
ms.fn();
System.out.println("msun ");
}
}
(3)我们可以将没有包括任何抽象方法的类声明为抽象类,以避免由这个类产生的任何对象。
4、native方法
JNI:java native interface
使用sun网站上下载的tutorial,目录下有native 1.1,打开index.html
指南里有具体的写一个java native 方法的步骤。
HelloWorld.java->HelloWorld.class->(javah -jni HelloWorld)HelloWorld.h+stdio.h具体操作见文档及孙鑫视频教程lesson3f.swf
static
{
System.out.println("sf");
}
//静态代码块。
21、关于protected访问控制符的说明
可访问性: public > protected > package >private
1.protected 访问控制符既能被用于方法又能用于成员变量。(不用于类)
2.声明为protected的方法和成员变量能被同一个包里的所有类所访问。
3.声明为protected的方法和成员变量能被该类的子类所访问,子类和父类可以不在一个包中。
4.不在同一个包中的子类要访问父类的方法或成员变量有2种方式.
(1) 直接访问父类的protected方法或成员变量。
// 定义父类;
package packfather;
public class Father(){
protected int var;
}
// 定义子类一;
package packsun;
import packfather.Father;
public calss Sun1 extends Father{
public void set(int varsun1){
// 可以直接访问父类protected变量;
var = varsun1;
}
}
(2) 通过子类的对象访问父类的protect方法或成员变量。
// 定义子类二
package packsun;
import packfather.Father;
public calss Sun2 extends Father{
pbulic void set(int varsun2){
Sun2 sun2 = new Sun2;
//子类的对象访问父类的protected变量
sun2.var = varsun2;
//在子类中利用父类对象访问父类的protected变量是不行的。
Father fh = new Father();
fh.var=20;(编译会报错)
}
}
注:子类中用父类对象和其它子类的对象都不能访问父类中的protected变量;
当你想让一个类中的某个方法或成员变量在包中都可见,而且其子类也能访问(子类有可能和父类不在同一个包中)但又不想让所有类都可以访问该类时,就可以用protected修饰符。
22、垃圾回收garbage collecter
java.lang.Object.finalize();
java.lang.System.gc();
当内存空间不足时,系统会自动调用垃圾回收器将不再使用的对象进行回收。系统会调用java.lang.System.gc();方法,然而在此之前还会调用java.lang.Object.finalize().
因为所有内都是继承自Object类,所以我们可以通过覆盖finalize方法来演示java系统进行垃圾回收的过程。
class Garbage
{
int index;
static int count;
Garbage()
{
count++;
System.out.println("object "+count+" constructed");
setID(count);
}
void setID(int id)
{
index=id;
}
protected void finalize()
{
System.out.println("object "+index+" reclaimed");
}
public static void main(String[] args)
{
new Garbage();//创建第一个对象,但没有引用,按理是要回收的,但JAVA不会立即回收,所以自己通过覆盖finalize方法和调用gc方法手动测试回收的过程。
System.gc();
new Garbage();
new Garbage();
System.gc();
}
}
运行结果为:
object 1 constructed
object 1 reclaimed
object 2 constructed
object 3 constructed
object 3 reclaimed
object 2 reclaimed
23、interface接口
(1)类可以实现一个或多个接口,需要注意的是:如果类要实现一个接口的话,必须要实现此接口中所有的方法。
interface Sport
{
void jump();
void run();
}
interface Eate
{
void eate();
}
class Athlete implements Sport,Eate
{
public void jump()
{
System.out.println("jump");
}
/*
public void run()
{
System.out.println("run");
}
*/
public void eate()
{
System.out.println("eate");
}
public static void main(String[] args)
{
System.out.println("Hello World!");
Athlete ah = new Athlete();
ah.jump();
// ah.run();
//对方法加注释后,会报错提示:Athlete.java:11: Athlete 不是抽象的,并且未覆盖 Sport 中的抽象方法 run(),即应该要实现接口中所有的方法才行。
ah.eate();
}
}
(2)接口中的方法都是public和abstract抽象的。接口中方法不能声明为:native、static、final、synchronized、private、protected等修饰符。
需要注意的是在类中实现方法的时候权限都要用public,现在设为默认权限试试:
interface Sport
{
void jump();//接口隐藏了修饰符:public abstract
void run();
}
interface Eate
{
void eate();
}
class Athlete implements Sport,Eate
{
void jump() //在上例的基础上将该方法设为默认权限
{
System.out.println("jump");
}
public void run()
{
System.out.println("run");
}
public void eate()
{
System.out.println("eate");
}
public static void main(String[] args)
{
System.out.println("Hello World!");
Athlete ah = new Athlete();
ah.jump();
ah.run();
ah.eate();
}
}
编译时出错提示:Athlete.java:13: Athlete 中的 jump() 无法实现 Sport 中的 jump();正在尝试指定更低的访问权限;应为 public void jump()
为什么这里一定要用public呢?
这是因为:接口的的方法都是默认为public权限并且一定是抽象的方法。即interface Sport中的方法如jump(),应为:public abstract void jump();
这里因为接口中的方法权限为public的,所以在类中具体实现接口的方法的权限至少不能比public低。
(3)和public类一样,public接口必须放在与接口同名的文件名中。
(4)接口中可以有数据成员,但数据成员默认都是为:public static final类型的。
如例:CountArea.java
interface Math
{
double PI=3.1415926;//隐藏了修饰符:public static final
}
class Area implements Math
{
double roundArea(int round)
{
return PI*round*round;
}
}
class CountArea
{
public static void main(String[] args)
{
Area ar = new Area();
System.out.println(ar.roundArea(3));
System.out.println(ar.PI); //通过对象实例访问
System.out.println(Math.PI); //通过接口名访问
System.out.println(Area.PI); //通过实现接口的类名访问接口中的成员
}
}
在上例中通过实例和接口都可访问PI,证明实际声明的时候为static类型的。
(5)示例说明一下接口的含义:
接口:程序开发的时候模块与模块之间可能有不能的团队在做,那么就需要一方定义接口并实现接口,而另一方只需调用接口中的方法就可以了。
接口一定是一方实现它,另一方调用它。
下面通过四个java文件来说明显卡与主板之间通过接口来通信
一、接口类:VidoCard.java
//接口:显卡与主板能通过接口连在一起。
interface VidoCard
{
void display();
String getName();
}
二、显卡类:Dmeng.java
//帝盟为显卡的制造商,他实现了接口VidoCard
class Dmeng implements VidoCard
{
String name;
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public Dmeng()
{
name="Dmeng name's vidodisplay";
System.out.println(name);
}
public void display()
{
System.out.println("vidocard display is running");
}
}
三、主板类:Mainboard.java
//主板类,在此类中安装cpu和显卡。
class Mainboard
{
String strCPU;
VidoCard vc;//注意这里并没有实例化对象,只是引用了接口。
public void setCPU(String strCPU)
{
this.strCPU=strCPU;
}
public void setVidoCard(VidoCard vc)
{
this.vc=vc;
}
void run()
{
System.out.println(strCPU);
vc.display();
System.out.println("Mainboard is running");
}
}
四、通过cpu,显卡,主板组装成的电脑类:Computer.java
class Computer
{
public static void main(String[] args)
{
Dmeng dm = new Dmeng();
Mainboard mb = new Mainboard();
mb.setCPU("Intel's cpu");
mb.setVidoCard(dm);
mb.run();
}
}
运行结果:
Dmeng name's vidodisplay
Intel's cpu
vidocard display is running
Mainboard is running
上面四个文件,显卡制造商实现了显卡的功能,主板通过接口VidoCard显示,最后组装的电脑运行证明通过接口使设备能顺利运行。
(6)java中不允许类的多继承,但允许接口的多继承,即一个接口可继承自多个接口,且一个类可继承多个接口。
如下例:
接口类Good.java
interface Sitable
{
void site();
}
interface Sleep
{
void sleep();
}
public interface Good extends Sleep,Sitable
{
void goodbye();
}
实现接口的类GGood.java
public class GGood implements Good
{
public void site()
{
System.out.println("site");
}
public void sleep()
{
System.out.println("sleep");
}
public void goodbye()
{
System.out.println("goodbye");
}
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
(7) 接口的嵌套
如类:Sports.java
interface A
{
interface B
{
int f();
void fn();
}
void fm();
}
class Sports implements A
{
public void fm() { }
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.fm();
}
}
编译通过。也就是说当接口有嵌套时,实现哪个接口就实现该接口的方法,可以不管内层的接口。
再看下例:实现内层的接口
interface A
{
interface B
{
int f();
void fn();
}
void fm();
}
class Sports implements A.B //实现内层接口
{
public int f()
{
System.out.println("implements A.f()");
return 0;
}
public void fn() { }
//public void fm() { } //这里实现的是内层的接口,外层接口的方法可以不实现。
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.f();
}
}
下面实现外部类实现接口A.B,内部类实现接口A.
class InterTest //在类里面也可以声明嵌套的接口
{
interface A
{
interface B
{
int f();
void fn();
}
void fm();
}
}
class Sports implements InterTest.A.B
{
static class InnerSports implements InterTest.A //private static class InnerSports implements InterTest.A 这里还可声明为private噢,在外部类可就不可以噢。
//因为我在外部类的main方法中创建了内部类的对象,这里需要声明为static的,因为main是static的。
{
public void fm()
{
System.out.println("在内部类中实现内层接口A.B");
}
}
public int f()
{
System.out.println("在外部类中实现接口A");
return 0;
}
public void fn() { }
//public void fm() { }
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.f();
InnerSports InS = new InnerSports();
InS.fm();
}
}
输出结果为:
Hello World!
在外部类中实现接口A
在内部类中实现内层接口A.B
下例在外部类的非main方法中创建内部类的对象,所以内部类不用声明为static的。
class InterTest
{
interface A
{
interface B
{
int f();
void fn();
}
void fm();
}
}
class Sports implements InterTest.A.B
{
class InnerSports implements InterTest.A //因为我在外部类中的getInnerObject方法创建了内部类的对象,所以不声明为static
{
public void fm()
{
System.out.println("在内部类中实现内层接口A.B");
}
}
public int f()
{
System.out.println("在外部类中实现接口A");
return 0;
}
public void fn() { }
//public void fm() { }
public void getInnerObject()
{
InnerSports InS = new InnerSports();
InS.fm();
}
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.f();
s.getInnerObject();
}
}
下面看一下接口声明为private的情况
class InterTest
{
private interface A
{
interface B
{
void fn();
}
void fm();
}
public class InnerTest implements A //与私有接口在同一类中,可以访问
{
public void fm()
{
}
}
}
class Sports implements InterTest.A.B //与私有接口不在同一类中,不可访问
{
public void fn() { }
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.f();
}
}
输出提示:
Sports.java:22: InterTest.A 可以在 InterTest 中访问 private
class Sports implements InterTest.A.B //与私有接口不在同一类中,不可访问 ^
Sports.java:22: 此处需要接口
class Sports implements InterTest.A.B //与私有接口不在同一类中,不可访问 ^
Sports.java:29: 找不到符号
符号: 方法 f()
位置: 类 Sports
s.f();
^
这里其实很容易理解,接口的权限跟类相似,这里在类中如果用private定义接口,那么只有在本类中有访问权限。
再看一下,将内层接口声明为private的情况
class InterTest
{
interface A
{
private interface B //声明为private,提示出错
{
void fn();
}
void fm();
}
public class InnerTest implements A //与私有接口在同一类中,可以访问
{
public void fm()
{
}
}
}
class Sports implements InterTest.A.B //与私有接口不在同一类中,不可访问
{
public void fn()
{
System.out.println("in fn");
}
public static void main(String[] args)
{
System.out.println("Hello World!");
Sports s = new Sports();
s.fn();
}
}
输出结果为:
Sports.java:5: 非法的修饰符组合:public 和 private
private interface B
这里因为Interace里面默认为public,而对内层接口声明的却是private所以提示非法组合。
关于接口更多问题的测试:
一、接口继承时的问题:接口C继承了接口A,B,但A,B中都有一个同名的方法action()
package com.lwf;
public class Test {
public static void main(String[] args) {
D c = new D();
c.action();
}
}
interface A{
public void action();
}
interface B{
public void action();
}
interface C extends A,B{
}
class D implements C{
public void action() {
System.out.println("action");
}
}
运行正常输出结果为action
二、类D实现两个接口,两个接口中都有一同名方法action
package com.lwf;
public class Test {
public static void main(String[] args) {
D c = new D();
c.action();
}
}
interface A{
public void action();
}
interface B{
public void action();
}
class D implements A,B{
public void action() {
System.out.println("action");
}
}
运行正常输出action
三、类D继承类B,并且实现接口A,但类B和接口A中都有同名方法action
package com.lwf;
public class Test {
public static void main(String[] args) {
D c = new D();
c.action();
}
}
interface A{
public void action();
}
class B{
public void action(){
System.out.println("actionb");
}
}
class D extends B implements A{
public void action() {
System.out.println("action");
}
}
运行正常,输出action
24、内部类
在一个类的里面定义另一个类,这样在内里面定义的类叫内部类。
(1)内部类中可以访问外部类的私有成员。当然也指的是所有成员。
class Outer
{
private int index=100;
class Inner
{
void print()
{
System.out.println(index);
}
}
void print()
{
Inner in = new Inner();
in.print();
}
}
public class Test
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.print();
}
}
运行结果为:100
这说明Inner类中可以访问Outer类中的私有成员Index,实事上当在外部类里创建内部类后,在内存单元中将内部类通过外部类.this与外部类关联起来,即Outer.this。
即然内部内可以访问外部类的private成员,那么肯定可以访问其它成员,实际上内部类可以访问外部类的所有成员。
(2)当内部类和外部类中有相同的成员时,内部类怎样访问这些成员呢?
class Outer
{
private int index=100;
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
}
}
void print()
{
Inner in = new Inner();
in.print();
}
}
public class Test
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.print();
}
}
运行结果为:30,因为print方法中有局部变量index=30;
那么在内部类的成员与外部类的成员名相同时,怎样访问内部类本身的成员和外部类的成员呢?
System.out.println(this.index); //this 返回当前对象的引用
System.out.println(Outer.this.index); //Outer.this实际就是指向Outer类引用
class Outer
{
private int index=100;
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
void print()
{
Inner in = new Inner();
in.print();
}
}
public class Test
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.print();
}
}
运行结果:
30
50
100
(3)那么我想在其它类中实例化一个内部类的对象,应该怎么做呢?
public class Test
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.print();
Inner in1 = new Inner();//这样是否可以呢?
in1.print();
}
}
看上面在Test类中实例化一个内部类的对象,编译会错,提示找不到该类。
这时候有两个方法:
1、需要在Outer类中添加函数,返回值为Inner类的引用。然后引用in1实例。
2、在其它类中直接实例化In2实例,详细看下面示例。
class Outer
{
private int index=100;
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
void print()
{
System.out.println("outer print");
Inner in = new Inner();
in.print();
}
Inner getInner()//返回Inner类的引用,然后在Test类中通过Outer类的实例调用该方法
{
return new Inner();
}
}
public class Test
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.print();
Outer.Inner in1 = ou.getInner();//调用Outer类中的方法getInner,注意内部类的引用是用Outer.Inner类定义的。
in1.print();
//Outer.Inner in1 =new Inner();
//不能直接产生一个Inner类的对象,这是因为内部类的对象可以随意的访问外部类的成员,要产生内部类的对象必先产生一个外部类的对象。
//所以在其它类中实例化内部类的另一种方法是:Outer.Inner in2 = ou.new Inner();
Outer.Inner in2 = ou.new Inner();
in2.print();
}
}
结果输出:
30
50
100
30
50
100
30
50
100
(4) 在外部类中直接引用内部类
class Outer
{
private int index=100;
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
void print()
{
System.out.println("outer print");
Inner in = new Inner();
in.print();
}
Inner getInner()
{
return new Inner();
}
public static void main(String[] args)
{
Inner in1= new Inner();//在main中实例化Inner类,看提示什么?
in1.print();
}
}
在上面的几个例子中Outer类的print方法中实例化了Inner类的实例,那么在Outer类的main方法中能否实例化Inner类呢?
如果直接在Outer类中创建Inner类的对象,那么编译提示出错:Test.java:28: 无法从静态上下文中引用非静态 变量 this: Inner in1= new Inner();
那么在外部类中怎么访问内部类呢?
类似其它类中引用内部类Inner的方法,在外部类中同样有两种方法:
1.将Outer类中的main()方法改为:
public static void main(String[] args)
{
Outer ou = new Outer(); //先创建一Outer类的对象
Inner in = ou.getInner();//利用Outer类的对象引用调用函数使Inner类获得引用in
in.print();
}
即同(3)中一样,通过外部类的函数返回内部类的引用
2.在外部类中利用外部类的引用创建内部类的对象,将main方法改为:
public static void main(String[] args)
{
Outer ou = new Outer();
Inner in1 =ou.new Inner();
in1.print();
}
由(3)、(4)可见要想在别的类中(包括外部类和其它类)引用内部类,先要创建一个外部类的对象ou.再能过外部类与内部类的关系引用内部类中的成员及函数。
(5) 类可以放在方法、if语句中、句语块中。
如下:编译无错误
class Outer
{
private int index=100;
void fn()
{
if(true)
{
class Midlle
{
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
}
}
}
}
在方法fn()中定义一局部变量,并在Inner类中的print方法中输出代码如下:
class Outer
{
private int index=100;
void fn(int a)
{ int b;
if(true)
{
class Midlle
{
class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
b = 5;
System.out.println(b);
}
}
}
}
}
}
编译出错提示:Outer.java:19: 从内部类中访问局部变量 b;需要被声明为最终类型 ^ b = 5;
即当类放在方法内部而且需要在内部类中访问方法中定义的局部变量时,该变量必须声明为final类型。
所以以上方法fn应改为:void fn(final int a){final int b=0;....}
因为b声明为final类,所以print()中不能用b=5;
(6)可以把内部类当作外部内的一个方法来理解。
我们知道类的访问权限没有protected,方法才有,在这里内部类可以声明为protected.
class Outer
{
private int index=100;
protected class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
}
以上将类Inner声明为protected,编译无错误。
下面看一下内部类声明为protected后的权限:
class Outer
{
private int index=100;
protected class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
}
class Test
{
Outer ou = new Outer();
Outer.Inner in = ou.new Inner();
}
编译无错误:说明同一包中的类访问protected内部类是没问题的。当然在外面类Outer中访问更没有问题。
下面测试一下从别的包访问声明为protected的内部类。
类:Outer 在com.god包中;
package com.god;
public class Outer
{
private int index=100;
protected class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
}
类:Good 在com.good包中;
package com.g;
import com.god.*;
class Good
{
Outer ou = new Outer();
Outer.Inner in = ou.new Inner();
}
编译:E:/>javac -d . *.java
出错提示:
Good.java:6: com.god.Outer.Inner 可以在 com.god.Outer 中访问 protected
出错地方:Outer.Inner in = ou.new Inner();
可见在其他包中是不能够访问protected的内部类。
现将Inner类声明为private:
class Outer
{
private int index=100;
private class Inner
{
int index=50;
void print()
{
int index=30;
System.out.println(index);
System.out.println(this.index);
System.out.println(Outer.this.index);
}
}
}
class Test
{
Outer ou = new Outer();
Outer.Inner in = ou.new Inner();
}
出错提示为:Outer.java:20: Outer.Inner 可以在 Outer 中访问 private
Outer.Inner in = ou.new Inner();
^
说明声明为private后,只有在外部类才可访问内部类,在同一包中的其他类也是没有权限访问的。
同理可以声明内部类为abstract,那么此类就不能在外部类和其他类中创建对象,要使用他,可以在其它类中继承内部类,并实现其中的方法。
声明内部类为final,即希望内部类不能被派生。
嵌套类:声明为static的内部类,则在内部类与外部类之间就构成实际的嵌套关系。
声明为static类,则不需要在其它类中通过创建内部类的对象来访问他,只需通过类名来引用就可以了。不过因为内部类是static修饰的,它不能访问外部类的非static成员。
注意:非static内部类中不能有static的声明。即在内部类中不能声明static的方法或成员。
例如:
class Outer
{
private int index=100;
class Inner //非static内部类
{
int index=50;
static void print()//声明为static
{
int index=30;
System.out.println(index);
System.out.println(this.index);//不能访问非static成员
System.out.println(Outer.this.index);
}
}
}
class Test
{
Outer ou = new Outer();
Outer.Inner in = ou.new Inner();
}
出错提示:
Outer.java:11: 无法从静态上下文中引用非静态 变量 this
System.out.println(this.index);
^
Outer.java:12: 无法从静态上下文中引用非静态 变量 this
System.out.println(Outer.this.index);
^
Outer.java:7: 内部类不能有静态声明
static void print()
^
在第三个错误中提示了:内部类不能有静态声明!!(前提是非static类中)
在static内部类中是可以声明static方法的:
class Outer
{
private int index=100;
static class Inner //声明为static内部类
{
int index=50;
static void print()
{
int index=30;
System.out.println(index);
//System.out.println(this.index);
//System.out.println(Outer.this.index);
}
}
}
(7)继承内部类
示例:
class Outer
{
class Inner
{
}
}
class Test extends Outer.Inner
{
public static void main(String[] args)
{
Test te = new Test();
}
}
出错提示:Outer.java:8: 需要包含 Outer.Inner 的封闭实例
class Test extends Outer.Inner
我们知道创建派生类的实例时会调用基类的构造函数,这里要调用内部类的构造函数必须要有一个从外部类到内部类的引用,注意这里与从其他类继承嵌套接口不同。
通过内部类与外部类的关系我们知道,要定义一下内部类的实例必须先要有一外部类的实例
这里修改如下:
class Outer
{
class Inner
{
}
}
class Test extends Outer.Inner
{
Test(Outer ou)
{
ou.super();//通过super的特殊应用,来建立内部类对象到外部类对象的引用关系。
}
public static void main(String[] args)
{
Outer out = new Outer(); //先实例化外部类对象
Test te = new Test(out); //传递外部类的引用作为参数
}
}
编译无错误。
(8)内部类实现接口
例:Test.java
interface Animal
{
void eate();
void sleep();
}
class Zoo
{
class Tiger implements Animal //内部类实现接口
{
public void eate()
{
System.out.println("eate");
}
public void sleep()
{
System.out.println("sleep");
}
}
Animal getAnimal()
{
return new Tiger(); //返回对象引用。这里因为类Tiger实现了接口的方法,所以可以直接用Animal getAnimal().之前我们用Tiger getTiger().
}
}
class Test
{
public static void main(String[] args)
{
System.out.println("main");
Zoo z = new Zoo();
Animal an = z.getAnimal();
an.eate();
an.sleep();
}
}
结果为:
main
eate
sleep
(9) 匿名内部类
在(8)中通过
Animal getAnimal()
{
return new Tiger(); //返回对象引用。这里因为类Tiger实现了接口的方法,所以可以直接用Animal getAnimal().之前我们用Tiger getTiger().
}
方法实现。
那么可否这样呢?如下:
Animal getAnimal()
{
return new Animal();
}
我们知道接口是不能够直接实例化一个对象的,所以在这里用,return new Animal()原则上是不行的。那么我们可以在这个语句结束之前先实现接口中的方法,那么就可以了。
我们在return new Animal()后加上{},并在{}中加上实现的方法。
代码如下:
interface Animal
{
void eate();
void sleep();
}
class Zoo
{
/*
class Tiger implements Animal
{
public void eate()
{
System.out.println("eate");
}
public void sleep()
{
System.out.println("sleep");
}
}
*/
//在下面用匿名内部类实现
Animal getAnimal()
{
return new Animal()
{
public void eate()
{
System.out.println("niming eate");
}
public void sleep()
{
System.out.println("niming sleep");
}
};
}
}
class Test
{
public static void main(String[] args)
{
System.out.println("main");
Zoo z = new Zoo();
Animal an = z.getAnimal();
an.eate();
an.sleep();
}
}
结果为:
main
niming eate
niming sleep
在代码中:
return new Animal()
{
public void eate()
{
System.out.println("niming eate");
}
public void sleep()
{
System.out.println("niming sleep");
}
};
实际上为一匿名内部类。最后分号不能少。
上面的这段代码的具体实现是:
class MyAnimal implements Animal
{
public void eate()
{
System.out.println("niming eate");
}
public void sleep()
{
System.out.println("niming sleep");
}
}
return new MyAnimal;
于是可以把上例程序改写为,思路会更清晰一些:
interface Animal
{
void eate();
void sleep();
}
class Zoo
{
Animal getAnimal()
{
class MyAnimal implements Animal
{
public void eate()
{
System.out.println("niming eate");
}
public void sleep()
{
System.out.println("niming sleep");
}
}
return new MyAnimal();
}
}
class Test
{
public static void main(String[] args)
{
System.out.println("main");
Zoo z = new Zoo();
Animal an = z.getAnimal();
an.eate();
an.sleep();
}
}
输出结果为:
main
niming eate
niming sleep
回答:在什么情况下要使用匿名内部类?
要在类中通过方法Animal getAnimal()返回接口的一个对象如return new Animal();,那么我必须要让编译知道我已经实现了接口中的方法,所以在方法的分号之前加入{}块。{}中为实现接口的代码。
这段{}的代码就是匿名内部类的实现。从上面具体实现我们知道,其实是在方法中定义一个内部类,类中实现接口中的方法,然后再返回这个内部类的引用。匿名内部类只是它的缩写而以。
(10)为什么要使用内部类
1.内部类中可以随意的访问外部类的成员,可以更好的组织管理代码,增强代码的可读性。
2。解决类的多重继承问题。
每个内部类可以独立的继承一个类,当一个类想要继承多个类的时候怎么办呢,显然只有外部类继承一个类是不够的,
我可以在这个类里定义多个内部类来实现。另外我可以在类中定义多个内部类来实现同一接口,但实现的方式不同。
我们说一个类实现两个接口可采用下面两种方法(1)是直接实现两个接口,(2)是实现一个接口,再利用匿名内部类返回另一接口的对象。
如:
interface A{}
interface B{}
(1) class X implements A,B {} //是直接实现两个接口,
(2) class X implements A{ //实现一个接口,再利用匿名内部类返回另一接口的对象
B MakB(){
return new B()
{
};
}
}
2.内部类可以用于创建适配器,适配器是可以实现接口的类,使用内部类来实现接口可以更好的定位与接口关联的方法在代码中的位置。
3.内部类的更多方法:
当一个类即要继承一个类,又要实现一个接口,但是所继承的父类及接口中有同名但内容不同的方法,这时应该使用内部类。
如:
interface Machine
{
void run();
}
class Person
{
void run()
{
System.out.println("heart run");
}
}
class Robot extends Person
{
class MachineHeart implements Machine
{
public void run()
{
System.out.println("run");
}
}
Machine getMachine()
{
return new MachineHeart();
}
}
class Test
{
public static void main(String [] args)
{
Robot ro = new Robot();
Machine ma = ro.getMachine();
ma.run();
ro.run();
}
}
结果为:
run
heart run
当一个类要继承多个类时:特别是其中有抽象类时
class A
{
void f1()
{
System.out.println("f1");
}
}
abstract class B
{
abstract void f2();
}
class C extends A
{
B getB()
{
return new B()
{
void f2(){System.out.println("f2");};
};
}
}
class Test
{
static void m1(A a)
{
a.f1();
}
static void m2(B b)
{
b.f2();
}
public static void main(String[] args)
{
C c = new C();
m1(c);
m2(c.getB());
}
}
输出:
f1
f2
25、异常处理
1、概念
JAVA程序在执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给JAVA运行时系统,这个过程称为抛出throw异常。
当JAVA运行时系统接收到异常对象时,会寻找能处理这一异常的代码,并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
如果JAVA运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的JAVA程序也将退出。
try/catch/finally
将有可能引发异常的语句放在try{}中,在catch语句中的捕获异常并进行处理。
需要注意的是:try语句块中一旦异常产生了,则此语句后面的语句不再执行。即当异常发生立刻跳转到catch中进行处理。
例如:
class Exce
{
public int division(int a,int b)
{
return a/b;
}
}
class ExcepTest
{
public static void main(String[] args)
{
Exce ex = new Exce();
try
{
ex.division(5,0); //调用函数,但分母为0,显然会出错,将可能出错的语句放在try块中。
System.out.println("trow"); //注意运行结果:并没有输出thow,因为上面的一条语句产生了异常,立刻转到catch块中处理。
}
catch (Exception e)
{
System.out.println(e.getMessage()); //为exception类从thowrable继承而来的方法,可以查找api中的java.lang.exception类。
System.out.println(e.toString());
}
finally
{
System.out.println("good"); //可以看到,不管异常是否发生都会输出good.
}
}
}
输出结果:
/ by zero //被0除
java.lang.ArithmeticException: / by zero
good
如果想知道异常所发生的位置可在catch块中调用方法e.printStackTrace();
catch (Exception e)
{
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace(); //打印出错信息的位置。因为printStackTrace方法本身有向标准错误输出打印信息的功能,所以不用System.out.println();
}
输出结果为:
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Exce.division(ExceTest.java:5)
at ExcepTest.main(ExceTest.java:15)
good
2、查看exception类的api说明可以看到exception类有许多子类,上例中具体的出错信息是ArithmeticException子类的内容,那么这里也可以修改异常参数
即将catch (Exception e)改为catch (ArithmeticException e),输出结果与上例一样。
3、异常的捕获也可放在方法中,如上例中可把异常捕获放在division方法中。
如:
class Exce
{
public int division(int a,int b)
{
try
{
return a/b;
//ex.division(5,0);
}
catch (ArithmeticException e)
{
e.printStackTrace();
return 0; //需要注意的是这里增加了返回语句,因为上面return a/b实际上先执行a/b就出现异常了,转到catch块,而函数又要求返回int类型值,所以必须要增加返回语句。
}
finally
{
System.out.println("good");
}
}
}
4、抛出异常,
I。可以在可以产生异常的函数中抛出异常,它将向上一级调用语句抛出此异常,要求上一级调用语句中要有捕获异常的动作。
class Exce
{
public int division(int a,int b) throws Exception //向调用它的语句抛出异常
{
return a/b;
}
}
class ExcepTest
{
public static void main(String[] args)
{
Exce ex = new Exce();
ex.division(5,0); //该处没有捕获和处理。
}
}
输出结果:
jasmine.jar
ExceTest.java:15: 未报告的异常 java.lang.Exception;必须对其进行捕捉或声明以便抛出
ex.division(5,0);
^
II。另一个需要注意的地方是:抛出的异常类型要与捕获的异常类型一致。如下:
class Exce
{
public int division(int a,int b) throws Exception //抛出异常Exception
{
return a/b;
}
}
class ExcepTest
{
public static void main(String[] args)
{
Exce ex = new Exce();
try
{
ex.division(5,0);
}
catch (ArithmeticException e) //捕获时是另一个异常
{
System.out.println(e.getMessage());
}
finally
{
System.out.println("good");
}
}
}
输出结果提示未报告的异常,这里抛出异常的类型应与捕获时一致。
这里也可以采用多个catch块来捕获如:
try
{
ex.division(5,0); //调用函数,但分母为0,显然会出错,将可能出错的语句放在try块中。
System.out.println("trow"); //注意运行结果:并没有输出thow,因为上面的一条语句产生了异常,立刻转到catch块中处理。
}
catch (ArithmeticException e)
{
System.out.println(e.toString());
}
catch (Exception e)
{
System.out.println(e.getMessage()); //为exception类从thowrable继承而来的方法,可以查找api中的java.lang.exception类。
}
finally
{
System.out.println("good"); //可以看到,不管异常是否发生都会输出good.
}
但是针对这个示例需要注意了,如果我把两个catch块换个位置,如下
catch (Exception e)
{
System.out.println(e.getMessage()); //为exception类从thowrable继承而来的方法,可以查找api中的java.lang.exception类。
}
catch (ArithmeticException e)
{
System.out.println(e.toString());
}
这样输出会提示:异常已经被捕获,这是因为所有异常类型都是Exception派生出来的。只要产生异常catch (Exception e)都能捕获,那么后面的捕获语句将不会执行。
所以写多个catch语句的时候注意,先将具体的异常放在前面,最后才放catch (Exception e)。
III。抛出异常,但不提示需要捕获的情况
class Exce
{
public int division(int a,int b) throws ArithmeticException
{
return a/b;
}
}
class ExcepTest
{
public static void main(String[] args)
{
Exce ex = new Exce();
}
}
这里抛出了ArithmeticException,但并没有提示出错信息,而在上面I中,抛出Exception异常时,系统会提示一定要捕获,这是为什么呢?
在api中查找ArithmeticException,不难发现它是继承自java.lang.RuntimeException,JAVA中RuntimeException异常一般是程序员自己因为水平不高而产生的异常,也就是说这一类
异常系统会自已进行处理。所以以后由RuntimeException派生的异常抛出的时候,不会要求一定要进行捕获!!
IIII.对子类抛出异常的限定:
如果父类中的方法抛出多个异常,则子类中的覆盖方法要么抛出相同的异常,要么抛出异常的子类,但不能抛出新的异常。(构造方法除外,因为构造方法是不能继承的)。
!!!几个重要的异常机制及问题
1> try{return;}finally问题
public class TEST{
public static void main(String[] args){
try{
return;
}catch(Exception e){
}finally{
System.out.println("finally");
}
}
}
运行结果:finally
但再问finally是在return前执行还是在return后执行?
Finally在return前执行,设计测试代码如下:
public class TestChart1 {
public static void main(String[] argc){
System.out.println("start:");
try{
System.out.println("before return:");
return ;
// System.out.println("after return:");
}finally{
System.out.println("end:");
}
// System.out.println("last:");
}
}
上面程序在注释的两句均会提示不能到达的区域。如果是先return再执行finally那么last肯定是可以输出的,但这里居然说这个没办法到达,说明是先就执行了finally,再执行return退出。
2> Exception
Throwable
| | |
| | |
Error Exception RuntimeException
Error系统错误,处理不了
Exception 可处理也必须处理的异常
RuntimeException 经常性出现的异常,太多。。可以catch也可以不catch。如数组越界,除零等
抛出异常:throws Exception
当方法本身无法处理异常时,可以抛出异常,供调用该方法的代码块处理异常,如果调用他的代码块还不能处理可继续抛出。
如:
import java.io.*;
public class TestTry{
public static void main(String[] args) {//throws Exception {
Cat c = new Cat();
//c.fun(); //调用方法:要么catch异常,要么在main方法中抛出异常throws Exception,但最好自己catch进行处理。
try{
}catch(Exception e){
e.printStackTrace();
}
}
}
class Cat{
public void fun() throws Exception{ //抛出异常
File name = new File("good.txt");
System.out.println("Exception");
}
}
3> 关于接口与实现类中的异常处理
public class TestTry{
public static void main(String[] args) {//throws Exception {
Cat c = new Cat();
c.fun();
}
}
interface CatInterface{
void fun() throws Exception;
void fun2();
}
class Cat implements CatInterface{
public void fun(){
System.out.println("fun");
}
//接口中抛出了异常,在实现类中可以不抛出或catch异常,但如果在实现类中相应方法头部抛出了异常,则在调用该方法的地方要catch异常或在main()后抛出异常。
//public void fun() throws Exception{
// System.out.println("fun");
//}
//public void fun2() throws Exception{
//}
//接口中此方法没有抛出异常,编译提示:未实现方法fun2();
//说明当接口中没有抛出异常时,想要实现接口,则实现类中该方法也不能在方法头部抛出异常。
public void fun2(){
}
}
>>> //得出结论:接口抛,实现可抛可不抛,接口不抛,实现一定不抛
4> 用于继承或实现接口中重写方法的规定:
实现接口或从父类继承重写方法需要抛出与原方法所抛出异常类型一致异常或不抛出异常.
26、java的常用包
java.applet:包含一些用于创建JAVA小应用程序的类。
java.awt:包含一些用于编写与平台无关的图形界面(GUI)应用程序的类。
java.io:包含一些用于输入输出I/O处理的类。
java.lang:包含一些JAVA语言的基本类与核心类,如String、Math、Integer、System和Runtime提供常用的功能,这个包被隐式导入。
java.net:包含用于建立网络连接的类,与JAVA。IO同时使用完成与网络有关的读写。
java.util:包含一些实用工具和数据结构类。
27、==与equals的区别
==主要用来比较两个变量的值;
equals比较两个对象变量所代表的对象的内容是否相等
JAVA中除8种基本类型外其它均为引用类型。
当我们声明一个引用类型变量时,系统只为该变量分配了引用空间,并未创建具体的对象,当用new为对象分配空间后将对象的引用赋值给引用变量。
引用变量可比拟为电视机的遥控器,而具体的对象则可比拟为电视机。
class StringCompare
{
public static void main(String[] args)
{
String str1 = new String("abc");
String str2 = new String("abc");
if(str1==str2)
System.out.println("str1==str2");
else
System.out.println("str1!=str2");
}
}
输出结果为:str1!=str2,
在这里==比较的是str1和str2两个变量的值。String str1;此语句会在栈内存空间中分配空间给变量str1,两个变量的值肯定是不一样的。但两个变量所指的值是一样的。
class StringCompare
{
public static void main(String[] args)
{
String str1 = new String("abc");
String str2 = new String("abc");
if(str1==str2)
System.out.println("str1==str2");
else
System.out.println("str1!=str2");
if(str1.equals(str2))
System.out.println("str1==str2");
else
System.out.println("str1!=str2");
}
}
输出内容为:str1!=str2
str1==str2
28、在JAVA中系统重载了+和+=运算符,但不允许程序员重载操作符。JAVA中String类型可与其它任何类型进行+,+=操作。
String 类代表字符串,为常量,声明为public final class String,是不可修改的。
在处理大量字符串时常用StringBuffer来代替String,通常用StringBuffer类的append方法和toString方法来实现String中的+操作以及向String的转换。
class StringCompare
{
public static void main(String[] args)
{
String str1 = new String("abc");
System.out.println(str1+1+1.0+'a'+true);
StringBuffer str2 = new StringBuffer();
System.out.println(str2.append(str1).append(1).append(1.0).append('a').append(true).toString());
}
}
输出结果:
abc11.0atrue
abc11.0atrue
说明使用String的+与StringBuffer中采用append方法效果是一样的。StringBuffer的初始容易为16个字符,但如果需要他会自动增加。
StringBuffer中有很多方法可以使用,具体查api文档。如delete,insert等
如:
class StringCompare
{
public static void main(String[] args)
{
String str1 = new String("abc");
System.out.println(str1+1+1.0+'a'+true);
StringBuffer str2 = new StringBuffer();
str2.append(str1).append(1).append(1.0).append('a').append(true);
System.out.println(str2);
str2.delete(2,4);
System.out.println(str2);
}
}
输出结果为:
abc11.0atrue
abc11.0atrue
ab1.0atrue
注意查api中delete方法为:public StringBuffer delete(int start,int end)
start从0开始:如上例中:abc11.0atrue,delete(2,4)指删除原字符串中从第二个开始到第四个字符,注意是包括第二个,但不包括第四个。
即2,4 指2<=x<4之间的字符。
29、main方法的详细讲解
public static void main(String[] args)
因为main方法是由jvm调用的。不需要产生任何对象所以声明为static的,又由于没有返回值,所以声明为void;
String[] args为一字符串数组,用于接收命令行输入的参数。
class Test
{
public static void main(String[] args)
{
System.out.println("Hello World!");
System.out.println(args[0]);
}
}
运行结果出错提示:
Hello World!
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0 at Test.main(Test.java:6)
即字符串数组并没有值。
参数args用于接收命令行输入的参数,但并不是java Test
而是后面跟的字符,如执行字节码文件Test.class时,使用如下命令,得结果为:
E:/JAVA TEST>java Test afafaf
Hello World!
afafaf
由此可知args接收了参数afafaf,从而经System.out.println(args[0]);输出结果为:afafaf
同理可利用数组接收多个参数,然后在main函数中输出。
30、参数传递
基本数据类型在参数传递的时候传递的是值;引用类型在参数传递的时候传递的是引用,引用相当于首地址。
31、对象克隆
I.为获得一份对象的拷贝,可利用Object类的clone().
II。在派生类中覆盖基类的clone方法并声明为public.
III。在派生类中通过super.clone()调用基类的clone方法。抛出异常或捕获异常CloneNotSupportedException
IIII。在派生类中实现cloneable接口。
下面Student类覆盖基类的clone方法,并调用super.clone();方法调用基类方法,另外此类还实现了cloneable接口。
class Student implements Cloneable
{
String name;
int age;
Student(String name,int age)
{
this.name= name;
this.age = age;
}
public Object clone()
{
Object o = null;
try
{
o=super.clone();
}
catch (CloneNotSupportedException e)
{
System.out.println(e.toString());
}
return o;
}
}
public class Teacher
{
public static void main(String[] args)
{
Student s1 = new Student("zhang shan ",19);
Student s2 =(Student)s1.clone();
s2.name="likai";
s2.age=20;
System.out.println("name="+s1.name+",age=" + s1.age);
System.out.println("name="+s2.name+",age=" + s2.age);
}
}
输出结果为:
name=zhang shan ,age=19
name=likai,age=20
这说明发生了克隆,创建了对象的副本。
32、数组的相关操作(视频4下,Lesson4G)
JAVA中数组都有一属性length,用来获取数组中元素的个数。(注意数组中有length属性,而String有length()方法。
数组的复制:System.arraycopy();
数组的排序:Arrays.sort();
在已排序的数组中查找某个元素:Arrays.binarySearch();
I.数组复制
class ArrayTest
{
public static void main(String[] args)
{
int[] num1 = new int[]{1,2,3};
int[] num2 = new int[3];
System.arraycopy(num1,0,num2,0,num1.length);
for(int i=0;i
System.out.println(num2[i]);
}
}
}
输出结果:1
2
3
说明:public static void arraycopy(Object src,
int srcPos,
Object dest,
int destPos,
int length)
参数:
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目标数据中的起始位置。
length - 要复制的数组元素的数量。
抛出:
IndexOutOfBoundsException - 如果复制会导致对数组范围以外的数据的访问。
ArrayStoreException - 如果因为类型不匹配而使得无法将 src 数组中的元素存储到 dest 数组中。
NullPointerException - 如果 src 或 dest 为 null。
II。数组的排序及排序后搜索指定字符
java.util.Arrays.sort();
类:ArrayTest
import java.util.Arrays;
class ArrayTest
{
public static void main(String[] args)
{
int[] num1 = new int[]{3,2,1};
Arrays.sort(num1);
for(int i=0;i
System.out.println(num1[i]);
}
int index=Arrays.binarySearch(num1,2); //这里要搜索的值为2,返回所在数组的索引位置。这里2的索引为1.
System.out.println("index= "+index);
System.out.println("element= "+ num[index]);
}
}
输出结果:
1
2
3
index=1
element= 2
说明:
public static int binarySearch(int[] a,int key)
参数:
a - 要搜索的数组。
key - 要搜索的值。
注意对象数组的排序:
例如:
import java.util.Arrays;
class ArrayTest
{
public static void main(String[] args)
{
Student[] st1 = new Student[]{
new Student(1,23),
new Student(2,24),
new Student(3,25)
};
Arrays.sort(st1);
for(int i=0;i
System.out.println(st1[i]);
}
}
}
class Student
{
int num;
int age;
Student(int num,int age)
{
this.num=num;
this.age=age;
}
public String toString()
{
return "num="+num+","+"age="+age;
}
}
输出结果为:
Exception in thread "main" java.lang.ClassCastException: Student
at java.util.Arrays.mergeSort(Arrays.java:1156)
at java.util.Arrays.sort(Arrays.java:1080)
at ArrayTest.main(ArrayTest.java:11)
详细看一下sort针对对象类型的方法:
public static void sort(Object[] a)根据元素的自然顺序,对指定对象数组按升序进行排序。
数组中的所有元素都必须实现 Comparable 接口。此外,数组中的所有元素都必须是可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,e1.compareTo(e2) 不得抛出 ClassCastException)。
Comparable接口在java.lang包中。可以查看api知道String类也实现了这个接口,所以String类型数组是可以排序的。
int compareTo(T o)比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
修改代码,实现接口如下:
import java.util.Arrays;
class ArrayTest
{
public static void main(String[] args)
{
Student[] st1 = new Student[]{
new Student(4,"zhangshan"),
new Student(2,"lisi"),
new Student(2,"eisi"),
new Student(3,"wangwu")
};
Arrays.sort(st1);
for(int i=0;i
System.out.println(st1[i]);
}
}
}
class Student implements Comparable //实现接口
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return "num:="+num+","+"name:="+name;
}
public int compareTo(Object o) //覆盖方法
{
Student s=(Student)o;
return num>s.num? 1 : (num==s.num? 0 :-1); //制定自己的比较方法,按学号进行排序
}
}
输出结果为:
num:=2,name:=lisi
num:=2,name:=eisi
num:=3,name:=wangwu
num:=4,name:=zhangshan
即得到排序后的输出结果。
输出的结果当学号一致的时候没有按名字进行排序,那么下例实现这个效果:
import java.util.Arrays;
class ArrayTest
{
public static void main(String[] args)
{
Student[] st1 = new Student[]{
new Student(4,"zhangshan"),
new Student(2,"lisi"),
new Student(2,"eisi"),
new Student(3,"wangwu")
};
Arrays.sort(st1);
for(int i=0;i
System.out.println(st1[i]);
}
}
}
class Student implements Comparable //实现接口
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return "num:="+num+","+"name:="+name;
}
public int compareTo(Object o) //覆盖方法
{
Student s=(Student)o;
int result = num>s.num? 1 : (num==s.num? 0 :-1); //制定自己的比较方法,按学号进行排序
if(result == 0)
{
result = name.compareTo(s.name); //调用String类的compareTo方法
}
return result;
}
}
输出结果:
num:=2,name:=eisi
num:=2,name:=lisi
num:=3,name:=wangwu
num:=4,name:=zhangshan
33、基本数据类型与封装类
基本数据类型与封装类是一一对应的如:int 对应Integer类。那么他们之间怎么转换呢?
例如:
class Test
{
public static void main(String[] args)
{
int i=3;
Integer in = new Integer(i); //转换成Integer类型。
int j= in.intValue(); //转换成int
System.out.println(j);
}
}
其他类型与封装类都有类似的方法进行转型。
那么String类型与Integer类型有什么转换呢。
class Test
{
public static void main(String[] args)
{
Integer num = new Integer(1000);
int num1 = num.intValue();
System.out.println("Integer->int:"+num+"->"+num1); //Integer对象转化为int.
float num2 = num.floatValue();
System.out.println("Integer->float:"+num+"->"+num2); //Integer对象类型转化为float
String str1 = new String("123");
int an = Integer.parseInt(str1);
System.out.println("String->int:"+str1+"->"+an); //String类型转化为int
int itoString = 23;
String st1 = Integer.toString(itoString);
System.out.println("int->String:"+itoString+"->"+st1); //int类型转化为String
String str2 = new String("1234");
Integer in1 = Integer.valueOf(str2);
System.out.println("String->Integer:"+str2+"->"+in1); //String类转化为Integer
Integer in2 = new Integer(200);
String st2 = in2.toString();//st2为类的实例,直接调用方法就可。
System.out.println("Integer->String:"+in2+"->"+st2); //Integer类转化为String
}
}
输出结果为:
Integer->int:1000->1000
Integer->float:1000->1000.0
String->int:123->123
int->String:23->23
String->Integer:1234->1234
Integer->String:200->200
方法说明:
(1)public int intValue()以 int 类型返回该 Integer 的值。
(2)public float floatValue()以 float 类型返回该 Integer 的值。
(3)public static int parseInt(String s)
throws NumberFormatException将字符串参数作为有符号的十进制整数进行分析
参数:
s - 包含要分析的 int 表示形式的 String。
返回:
用十进制参数表示的整数值。
(4)public static String toString(int i)返回一个表示指定整数的 String 对象。将该参数转换为有符号的十进制表示形式,以字符串形式返回它
(5)public static Integer valueOf(String s)
throws NumberFormatException返回保持指定的 String 的值的 Integer 对象。将该参数解释为表示一个有符号的十进制整数, 就好像将该参数赋予 parseInt(java.lang.String) 方法一样。结果是一个表示字符串指定的整数值的 Integer 对象。
(6)public String toString()返回一个表示该 Integer 值的 String 对象。
34、CLASS类
在JAVA中,每个class都有一个相应的Class对象。我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
获取Class实例的三种方式:
(1)利用对象调用getClass()方法获取该对象的Class实例;
(2)使用Class类的静态方法forName(),用类的名字获取一个Class实例。
(3)运用。class的方式来获取Class实例,对于基本数据类型的封装类,还可采用.TYPE来获取相对应的基本数据类型的Class实例。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
以下示例使用 Class 对象来显示对象的类名:
void printClassName(Object obj) {
System.out.println("The class of " + obj +
" is " + obj.getClass().getName());
}
还可以使用一个类字面值(JLS Section 15.8.2)来获得命名类型(或 void)的 Class 对象。例如:
System.out.println("The name of class Foo is: "+Foo.class.getName());
示例:
class Test
{
public static void main(String[] args)
{
Point pt = new Point();
Class c1 = pt.getClass();
System.out.println(c1.getName());
try
{
Class c2 = Class.forName("Point");
System.out.println(c2.getName());
}
catch (Exception e)
{
e.printStackTrace();
}
Class c3 = Point.class;
System.out.println(c3.getName());
Class c4 = int.class;
System.out.println(c4.getName());
Class c5 = Integer.TYPE;
System.out.println(c5.getName());
}
}
class Point
{
int x,y;
}
输出结果为:
Point
Point
Point
int
int
视频4下j,k
35、ArrayList和Vector的区别,HashMap和Hashtable的区别
答:就ArrayList与Vector主要从二方面来说.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程不安全的,不是同步的,ArrayList效率高。要使用线程安全的同步列表使用:Collection.synchronizedList
二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
就HashMap与HashTable主要从三方面来说。
一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap可以让你将空值作为一个表的条目的key或value
36、 程序、进程、线程
程序是计算机指令的集合
进程是资源申请、调度和独立运行的单位。进程使用系统中的运行资源,程序不能申请系统资源。
线程是进程中一个单一的连续控制流程,一个进程可以拥有多个线程。线程没有独立的存储空间,而是和同属一个进程的线程共享一个存储空间。
JAVA在语言级支持多线程:
继承thread类:java.lang.thread,该类重写thread类的run方法
实现Runable接口
同步:有同步方法和同步块。
1、线程可理解为程序里不同的执行路径。声明一个Thread的实例即创建了一个线程,run方法中的内容为线程的执行内容。
要想实现多线程,即同时有其它线程与主线程并行执行,则要声明一个Thread类的实例。
其实实现Runnable接口后也要声明Thread的实例
一般先使用实现接口的方法,再考虑是否继承Thread类。
public class TestRunnable {
public static void main(String[] args) {
Runner r = new Runner();
Thread t = new Thread(r); //构造方法Thread t = new Thread(Runnable r);
t.start(); //new Thread(r).start();代替
for(int i=0;i<100;i++){
System.out.println("Thread main: "+i);
}
}
}
class Runner implements Runnable{
public void run(){ //注意如果不异常不能在这里使用public void run throws Exception,因为run是重写了Thread类的方法。
for(int i=0;i<100;i++){
System.out.println("Thread Runner: "+i);
}
}
}
2、理解主线程与其它线程执行步骤:
import java.util.Date;
public class TestRunnable {
public static void main(String[] args) {
Runner r = new Runner();
r.start(); //主线程与子线程并行执行开始
try {
Thread.sleep(1000); //主线程睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
r.interrupt(); //打断子线程,使子线程中断,return
System.out.println("main"); //现在只有主线程执行。
}
}
class Runner extends Thread{
public void run(){
while(true){
System.out.println(new Date());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
}
}
主线程执行
|
| 子线程执行
|------|
|sleep(1000) |
| |sleep(1000)
|调用 |
|r.interrupt |
| 打断子线程 |return子线程打断
|主线程打印main
3、join方法
public final void join()
throws InterruptedException等待该线程终止
在主线程中调用JOIN方法,即主线程先等待该线程执行完,再执行主线程。相当于将两个并行执行的线程变为一个线程。
4、yield方法
public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
5、线程互斥:
多个线程访问同一个资源的时候,要考虑同步的问题。如
银行取钱的问题:你和你朋友(你拿折,朋友拿卡)去取钱,同一个账号,
3000
|
| |
| |
你取2000 朋友拿卡取2000
如果不采用同步将共享的3000块锁定,那么你朋友取走2000块后你也取了2000块,那银行不亏死。。
可以将取钱的方法放在同步方法或同步块中,锁定共用的资源。当一线程执行时将共用的资源锁定,此时只有该线程有权执行,执行完后其它线程才执行。
锁定可采用同步的方法或同步块。指执行该方法的时候锁定了当前的对象。synchronized(this){}当执行这个块的时候锁定当前对象。
6、多线程程序在执行过程中会发生死锁:
注意理解死锁是在多线程的基础上发生的。。。
死锁理解为两个或多个线程相互锁定了对方所需求的资源。。。(如两个线程都分别需要两个资源才能进行,但各自都只有一个资源,另一个资源
被对方锁定了,这个时候就发生死锁。。
著名的哲学家就餐问题就是这样(多个线程死锁)。。两根筷子,自己有一根另一根在别人那里,大家都只有一根筷子最后死锁,都吃不了。。
如程序示例:
public class TestLock implements Runnable{
public int flag=1; //flag用于控制两个线程在run中执行的不同的块。
static Object o1 = new Object(), o2 = new Object();
public void run(){
System.out.println("flag:"+flag);
if(flag==1){
synchronized(o1){ //在这个块里包括另一个块,表示两个资源,必须都满足的时候才执行
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(o2){
System.out.println("1");
}
}
}
if(flag==0){
synchronized(o2){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(o1){
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
TestLock t1 = new TestLock();
TestLock t2 = new TestLock();
t1.flag=1; //t1执行flag=1的块
t2.flag=0; //t2执行flag=0的块
Thread th1 = new Thread(t1); //两个线程的创建
Thread th2 = new Thread(t2);
th1.start();
th2.start();
}
}
输出结果为:
flag:1
flag:0
后来就没反应了。。即发生了死锁。
7、线程同步:
同步指一个线程执行过程中要等待另一线程的执行。
下面的例子中Producer和Consumer两个线程共享了Query的资源。
生产者与消费者问题?
Producer为生产者类,Consumer为消费者类,Query队列类
public class TestThreads {
public static void main(String[] args) {
Query q = new Query();
Producer p = new Producer(q); //生产者与消费者两个线程使用的一定是同一个对象q.
Consumer c = new Consumer(q); //生产者与消费者两个线程使用的一定是同一个对象q.
p.start();
c.start();
}
}
class Producer extends Thread{
Query q;
Producer(Query q){
this.q=q;
}
public void run(){
for(int i=0;i<10;i++){
q.put(i);
System.out.println("producer:"+i); //打印出生产的东西。
}
}
}
class Consumer extends Thread{
Query q;
Consumer(Query q){
this.q=q;
}
public void run(){
while(true){
System.out.println("consumer"+q.get()); //打印出消费的东西。
}
}
}
class Query{
int value;
boolean bfull=false; //判断队列是否已满
public synchronized void put(int value){ //两个线程访问同一对象的资源,一定要用synchronized方法进行互斥。
if(!bfull){
this.value=value;
bfull=true;
notify();
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized int get(){
if(!bfull){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
bfull=false;
notify();
return value;
}
}
生产者与消费者另一实例:
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
}
}
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return "WoTou : " + id;
}
}
class SyncStack {
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt) {
while(index == arrWT.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
arrWT[index] = wt;
index ++;
}
public synchronized WoTou pop() {
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int)(Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
37、集合
java容器当中不能存放基本类型的数据,而只能存放内置类型的数据。
list 、set接口继承自Collection接口;
set不可重复,不包含重复的元素,SortedSet是一个按照升序排序元素的set.
list是一个有序的集合,但并不是排序的。。可包含重复元素。提供按索引访问的方式。(重复是指两个对象互相equals)
Map包含了KEY-VALUE对,MAP不能包含重复的KEY,SortedMap是一个按照升序排列key的map.
1、ArrayList:
看作是一个能够自动增长容量的数组。
利用ArrayList的toArray()返回一个数组
Arrays.asList()返回一个列表。
2、迭代器(Iterator)给我们提供了一种通用的方式访问集合中的元素。
Iterator调用remove()前先调用next().
3、类collections类
sort()方法用于对列表进行排序。
但类必须实现comparable接口跟Arrays类中的sort()方法一样。。
1、例:ArrayListTest.java
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList
al.add("adfsf");
al.add("dfe");
System.out.println(al);//two print method
for(int i=0;i
System.out.println(al.get(i));
}
}
}
例:ArrayListTest.java
此例表示在ArrayList中添加自定义类型的对象,并通过覆盖toString()方法打印出来
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList
al.add(new Point(23,45)); //添加对象类型
System.out.println(al);//two print method
for(int i=0;i
System.out.println(al.get(i));
}
}
}
class Point
{
int x,y;
Point(int x,int y)
{
this.x=x;
this.y=y;
}
public String toString()
{
return "x="+x+";y="+y;
}
}
在上例中加入toArray方法从列表中返回元素到数组中
例:ArrayListTest.java
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList
al.add(new Point(23,45));
al.add(new Point(232,434));
System.out.println(al);//two print method
for(int i=0;i
System.out.println(al.get(i));
}
Object[] o =al.toArray(); //调用ArrayList类中的toArray();
for(int i=0;i
System.out.println(o[i]);
}
}
}
class Point
{
int x,y;
Point(int x,int y)
{
this.x=x;
this.y=y;
}
public String toString()
{
return "x="+x+";y="+y;
}
}
使用Arrays.asList();从数组返回一个固定长度的列表
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList
al.add(new Point(23,45));
al.add(new Point(232,434));
System.out.println(al);//two print method
for(int i=0;i
System.out.println(al.get(i));
}
Object[] o =al.toArray();
for(int i=0;i
System.out.println(o[i]);
}
List