Java变量及其传递、内部类(4.9-4.10)

 
 

变量及其传递 & 内部类

 

一、 Java变量及其传递

1.1 基本数据变量与引用型变量

1. 📒Java变量:
 (1). 基本型变量(8种)
   char,byte,short,int,long,float,double,boolean
 (2). 引用型变量:对象,接口,数组

T2.png
N.png

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的实体值也就相应的改变了。
*/

FX.png

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();
}
}