变量及其传递 & 内部类
 
一、 Java变量及其传递 1.1 基本数据变量与引用型变量 1. 📒Java变量:  (1). 基本型变量(8种)    char,byte,short,int,long,float,double,boolean  (2). 引用型变量: 对象,接口,数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 举例: public class MyDate{ private int day=12; private int month=6; private int year=1900; public MyDate(int y,int m,int d){ year=y; month=m; day=d;} void addYear() { year++;} public void display (){ System.out.println(year+"-" +month +"-" +day); } public static void main(String[] args){ MyDate m,n; m=new MyDate(2003,9,22); n=m; "//n和m指向同一个地址" n.addYear(); m.display(); n.display(); System.out.println(m.toString()); System.out.println(n.toString()); } } /*运行结果 2004-9-22 2004-9-22 MyDate@11a698a MyDate@11a698a */
   
1.2 成员变量与局部变量 **成员变量与局部变量有没有区别?**
成员变量若为static的称为类变量,否则称为成员变量,而局部变量是在方法体内的变量。
(1) 📒从语法形式上看,成员变量是属于类或接口的,而局部变量是在方法中定义的变量或方法的参变量;
成员变量可以被public,private,static等修饰,而局部变量则不能被访问控制符及static修饰;成员
变量及局部变量都可以被final修饰。
(2) 📒从变量在内存中的存储方式看,成员变量是对象的一部分,而对象是存在于堆中的,而局部变量是
存在于栈中的。
(3) 📒从变量在内存中的存在时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变
量随着方法的调用而产生,随着方法调用结束而自动消失。
(4) 📒成员变量如果没有赋初值,则会自动以该类型的默认值(0,false,null等)赋值;而局部变量则不会
自动赋值,必须显示地赋值后才能使用。
1 2 3 4 5 6 7 8 class A{ int a; void m (){ int b; System.out.println(a); //合法的,a的值为0 System.out.println(b); //不合法,编译不通过 } }
   
1.3 变量的传递 1. 📒按值传递: 当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。(传递8种基本型变量)2. 📒按引用传递: 当将一个参数传递给一个函数时,函数接收的原始值的内存地址,而不是值的副本,因此,如果修改了该参数,调用代码中的原始值也随之改变。(传递对象,接口,数组)Java中的参数传递比C++简单,按值传递所有参数,制作所有参数的副本,而不管它们的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 例1: class TestTransOfValue { public static void main(String args[]) { int val; StringBuffer sb1, sb2; val = 10; sb1 = new StringBuffer("apples" ); sb2 = new StringBuffer("pears" ); System.out.println("val is " + val); System.out.println("sb1 is " + sb1); System.out.println("sb2 is " + sb2); System.out.println("" ); System.out.println("calling modify" ); //按值传递所有参数 modify(val, sb1, sb2); System.out.println("returned from modify" ); System.out.println("" ); System.out.println("val is " + val); System.out.println("sb1 is " + sb1); System.out.println("sb2 is " + sb2); } public static void modify(int a, StringBuffer r1, StringBuffer r2) { System.out.println("in modify..." ); //在方法中修改基本类型变量的值 a = 0; //在方法中修改对象引用值的副本 r1 = null; //1 //在方法中修改副本的对象实体值 r2.append(" taste good" ); System.out.println("a is " + a); System.out.println("r1 is " + r1); System.out.println("r2 is " + r2); } } 运行后输出: val is 10 sb1 is apples sb2 is pears calling modify in modify... a is 0 r1 is null r2 is pears taste good returned from modify val is 10 sb1 is apples sb2 is pears taste good /*关于这里的sb1和r1: 当参数传到modify后,r1和sb1指向同一个地址,若改变了r1指向的地址,并不会更改sb1指向的地址。这个时候就 相当于看作两个指针了。 关于sb2和r2: sb2和r2指向同一个地址,这个地址指向的值(实体值)被改变了,则结束后sb2和r2的实体值也就相应的改变了。 */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 例2: class Swap { public static void main(String args[]) { Integer a, b; a = new Integer(10); b = new Integer(50); System.out.println("before swap..." ); System.out.println("a is " + a); System.out.println("b is " + b); swap(a, b); System.out.println("after swap..." ); System.out.println("a is " + a); System.out.println("b is " + b); } public static void swap(Integer a, Integer b) { Integer temp = a; a = b; b = temp; System.out.println("in swap..." ); System.out.println("a is " + a); System.out.println("b is " + b); } } /* 运行后输出: before swap... a is 10 b is 50 in swap…a is 50 b is 10 after swap... a is 10 b is 50 */
**参数传递总结:**
(1) Java按值传递所有参数,制作所有参数的副本,而不管它们的类型
(2) 对于基本类型的变量形参修改了并不能反映到函数外面的实参
(3) 对于引用类型的变量,在形参中修改了对象实体值可以反映到实参,在形参中修改了对象引用值,
不能反映到实参
   
1.4 引用型变量的实体值、引用值比较 1. 📒参数传递总结: (1) 比较基本类型的数据: ==和!= (2) 如果要比较引用型变量是否相同,可以直接用==和!=吗? 答:不能
==和!=用来比较引用型变量时(只能比较引用值(地址)是不是相等),只能判断运算符两边引用的是不是同一个对象,即对象的地址值(或对象引用值)!!!!! 如何比较两个对象的内容(对象实体值)是否相同? 用equls()方法:
**2. 📒引用型变量比较总结:**
(1) 比较两个变量是否同一个对象(即对象引用值是否相同),用==和!=
(2) 比较两个变量的内容是否相同用equals方法
(3) **自己定义的类如果要支持equals方法必须重写从Object类继承来的equals方法(比如前面的String类
是java自己的类就不需要重写equals,但是Employee类就是自己定义的类则需要重写equals)**
Object类中的equals方法: public boolean equals(Object obj){ return (this==obj); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 equals示例1: class EqualsTest{ public static void main(String[] arguments) { String str1,str2; str1="Free the bound periodicals." ; str2=str1; System.out.println("String1: " +str1); System.out.println("String2: " +str2); System.out.println("Same object? " +(str1==str2)); //true str2=new String(str1); System.out.println("String1: " +str1); System.out.println("String2: " +str2); System.out.println("Same object? " +(str1==str2)); //false System.out.println("Same value? " +str1.equals(str2)); //true String str3,str4; str3="busy" ; str4="busy" ; //str4=new String("busy" )或str4=new String(str3) System.out.println("String3: " +str3); System.out.println("String4: " +str4); System.out.println("Same object? " +(str3==str4)); //true System.out.println("Same value? " +str3.equals(str4)); //true String str5,str6; str5="str5busy" ; str6="str6busy" ; System.out.println("String5: " +str5); System.out.println("String6: " +str6); System.out.println("Same object? " +(str5==str6)); //false System.out.println("Same value? " +str5.equals(str6)); //false } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 equals示例2: import java.util.*; public class EqualsTest { public EqualsTest () { } public static void main(String[] args){ Employee alice1=new Employee("Alice" ,75000,1987,12,15); Employee alice2=alice1; Employee alice3=new Employee("Alice" ,75000,1987,12,15); Employee bob=new Employee("Bob" ,50000,1989,10,1); System.out.println("alice1==alice2: " +(alice1==alice2)); //true System.out.println("alice1==alice3: " +(alice1==alice3)); //false System.out.println("alice1.equals(alice3): " +alice1.equals(alice3)); //true System.out.println("alice1.equals(bob): " +alice1.equals(bob)); //false System.out.println("bob.toString(): " +bob); } } class Employee{ private String name; private double salary; private Date hireDay; public Employee(String n,double s,int year,int month,int day){ name=n; salary=s hireDay=new Date(year,month,day); } public String getName (){ return name; } public double getSalary (){ return salary; } public Date getHireDay (){ return hireDay; } public void raiseSalary(double byPercent){ double raise=salary*byPercent/100; salary+=raise; } public boolean equals(Object otherObject){ //子类覆盖了Object的equals方法 if (this==otherObject) return true ; if (otherObject==null) return false ; if (getClass()!=otherObject.getClass()) return false ; Employee other=(Employee)otherObject; return name.equals(other.name)&& salary==other.salary&&hireDay.equals(other.hireDay); //子类覆盖了Object的toString方法 } public String toString (){ return getClass().getName()+ "[name=" +name+",salary=" +salary+",hireDay=" +hireDay+"]" ; } private String name; private double salary; private Date hireDay; }
   
二、 内部类 2.1 内部类的定义与使用 1. 📒内部类的定义: 将类的定义置入一个用于封装它的类(外部类)里。 注意: 内部类不能与外部类同名(否则,编译器无法区分内部类与外部类),如果内部类还有内部类,内部类的内部类不能与它的任何一层外部类同名。 作用: 逻辑分组, 隐藏细节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 /*内部类结合多态可以很方便的隐藏类的细节(包括类名) */ interface Contents{ int value(); } interface Destination { String readLabel(); } class Goods{ private class Content implements Contents{ //"private class Contentimplements Contents" private int i=11; public int value (){ return i; } } protected class GDestination //"protected class GDestination implements Destination" implements Destination{ private String label; private GDestination(String whereTo){ label=whereTo; } public String readLabel (){ return label;}} public Contents cont (){ return new Content(); } public Destination dest(String s){ return new GDestination(s); } } public class TestGoods { //"public class TestGoods" public static void main(String[] args){ Goods g=new Goods(); Contents c=g.cont(); Destination d=g.dest("Beijing" ); } }
2. 📒内部类对象的创建: 创建非静态内部类的对象时一定要确保已经有一个外部类对象。 (1) 利用外部类的方法创建并返回,因为方法是由外部类对象调用的,那创建该内部类对象时,一定已经拥有了所属的外部类对象了 public Contents cont(){      Goods g=new Goods(); return new Content();       Contents c=g.cont(); }
(2) 创建内部类还可以在除外部类中的其它类中,但是要确保该类具有访问内部类的权限,并且已经创建了一个外部类对象。格式如下:
1 2 3 4 5 6 7 8 outerObject=new outerClass(Constructor Parameters); outerClass.innerClass innerObject = outerObject.new InnerClass(Constructor Parameters); Goods g1=new Goods(); Goods.Content c1=g.new Content(); OR Goods g1=new Goods(); Goods.GDestination d1=g1.new GDestionation();
 3. 📒内部类的修饰符: (1)public、protected、private和缺省: 访问权限修饰符,用来限定内部类的访问权限,   一般的外部类是不能用protected和private修饰的。访问权限修饰符的限定和成员变量的限定一样。 (2)final: 表明内部类不能继承。 (3)abstract: 抽象内部类,不能被实例化。 (4)static: 表明一个静态内部类。
4. 📒静态内部类: 和普通的非静态内部类有较大的不同,使用时要遵循如下原则: (1)实例化static内部类时,在new前面不需要用对象变量;(2)static内部类中不能访问其外部类的非static属性及方法,即只能访问static成员; (3)static方法中不能访问非static的属性及方法,也不能不带前缀地new一个非static的内部类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class A { private int x; void m (){ new B(); } static void sm (){ //在外部类的静态方法中 //不能直接创建内部类对象 //new B();//!!error } class B { B (){x=5;} }//B类结束 }//A类结束 class Outer { static class Inner { } } class TestInnerStatic { public static void main(String[] args) { //创建非静态内部类B的对象a_b和ab A.B a_b=new A().new B(); A a=new A(); A.B ab=a.new B(); //创建静态内部类Inner的对象oi,不需要外部类Outer的对象 Outer.Inner oi=new Outer.Inner(); //Outer.Inner oi2=Outer.new Inner();//!!error //Outer.Inner oi3=new Outer().new Inner();//!!error } }
 5. 📒内部类中访问外部类的成员: (1) 内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。 (2) 如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法: outerClass.this
   
2.2 方法和作用域中的内部类 内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。 (1) 内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。 (2) 如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法: outerClass.this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 例1: 方法中的内部类: interface Destination { String readLabel(); } public class Goods2 { String a="Goods2" ; public Destination dest(String s) { class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; System.out.println("access outer class:" +a); } public String readLabel () { return label; } } return new GDestination(s); } public static void main(String[] args) { Goods2 g= new Goods2(); Destination d = g.dest("Beijing" ); } } /*在方法dest()中定义了一个内部类,最后由这个方法返回这个内部类的对象。如果在用一个内部类的时候仅需要创建它的一个对象并传给外部,就可以这样做。 */
 方法中定义内部类的注意点: (1) 方法中定义的类,在其它地方使用时,没有类的名字,一般用其父类来引用这样的变量。 (2) 同局部变量一样,方法中的内部类前面不能用public,private,protected修饰,也不能用static修饰,但可以被final或abstract修饰。 (3) 方法中的内部类,可以访问其外部类的成员;若是static方法中的内部类,可以访问外部类的static成员。 (4) 方法中的内部类中,不能访问该方法的局部变量,除非是final的局部变量。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 例2: 作用域中的内部类: public class Goods3{ private void internalTracking(boolean b) { if (b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip () { return id; } } TrackingSlip ts = new TrackingSlip("slip" ); String s = ts.getSlip(); } } public void track () { internalTracking(true ); } public static void main(String[] args) { Goods3 g= new Goods3(); g.track(); } }
   
2.3 匿名内部类 匿名类: 类或方法中定义的一种没有类名的特殊内部类。作用: 当需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。语法规则如下: new interfacename(){……}; 或new superclassname(){……};
注意: (1) 这种类不取名字,而直接用其父类的名字或者它所实现的接口的名字; (2) 类的定义与创建该类的一个对象同时进行,即类的定义前面有一个new,没有类的首部,对象的创建和类体共同构成一个匿名类表达式,后面以“;”结束; (3) 类中不能定义构造方法,因为它没有名字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface Contents{ int value(); } public class Goods4 { public Contents cont (){ //返回匿名类对象,该匿名类继承了Contents接口 return new Contents (){ private int i = 11; public int value () { return i; } }; } public static void main(String[] args) { Goods4 g=new Goods4(); Contents c=g.cont(); } }