基础篇_02的主要内容
- 1、引用传递和值传递
- 2、链表
一丶引用传递和值传递
1、基本类型和引用类型在内存中的保存
|- Java中数据类型分为两大类,【基本类型】和【对象类型】。
基本类型的变量保存原始值,即它代表的值就是数值本身;
这里的基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress,etc...
引用类型的变量保存引用值。
"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
引用类型包括:类类型,接口类型和数组。
2、变量的基本类型和引用类型的区别
|- 变量也有两种类型:【基本类型】和【引用类型】。
基本数据类型在声明时系统就给它分配空间:
int a;
a=10;//正确,因为声明a时就分配了空间
引用就不相同了,它声明时只给变量分配了引用空间,而不分配数据空间:
Date date;
date=new Date();
date.getDate();
//执行实例化,开辟数据空间存放Date对象,然后把空间的首地址传给“今天到的日期”变量
System.out.println(date.getDate());//输出今天的日期
/*
*如果注释掉date=new Date();就会有异常抛出
*NullPointerException,意思就是说一个空的变量,一个空的栈内存。
*The local variable date may not have been initialized
*也就是说对象的数据空间没有分配
*/
3、引用传递和值传递
引用传递是Java的核心问题,引用传递的操作的核心就是内存地址的传递。
这里要用 【实际参数】 和 【形式参数】的概念来帮助理解,
值传递:
方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。
-
范例:
-
public class Test01 {
public static void change(int a){
a=10000;
}
public static void main(String[] args) {
int a=10;
System.out.println(a);//10
change(a);
System.out.println(a);//10
}
}
改变的是形式参数的值,结果不影响实际参数。
引用传递:也称为传地址。
方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。
-
范例:
-
public class Test02 {
public static void change(int []a){
a[0]=50;
}
public static void main(String[] args) {
int []a={10,20};
System.out.println(a[0]);//10
change(a);
System.out.println(a[0]);//50
}
}
这里传来的是地址,形式参数和实际参数有一样的地址,改变了地址中的内容
通过以下三个Demo来说明作用
class Demo{
private int count = 10 ;
public void setCount(int count){
this.count = count ;
}
public int getCount(){
return this.count ;
}
};
public class Demo01 {
public static void main(String args[]){
Demo d1 = new Demo() ;
d1.setCount(100) ;
fun(d1) ;
System.out.println(d1.getCount()) ;
//输出的结果为30
}
public static void fun(Demo d2){
d2.setCount(30) ;
}
};
public class Demo02 {
public static void main(String args[]){
String str = "hello" ;
fun(str) ;
System.out.println(str) ;
//输出的结果是hello
}
public static void fun(String temp){
temp = "world" ;
}
};
字符串的内容无法改变,改变的只是内存地址的指向。
class Demo{
private String str = "hello" ;
public void setStr(String str){
this.str = str ;
}
public String getStr(){
return this.str ;
}
};
public class Demo03 {
public static void main(String args[]){
Demo d1 = new Demo() ;
d1.setStr("world") ;
fun(d1) ;
System.out.println(d1.getStr()) ;
//最后的输出结果是!!!
}
public static void fun(Demo d2){
d2.setStr("!!!") ;
}
};
二丶链表的认识(这里是说一个指针域的)
1、链表是一个个结点连接起来的长链。
链表包括两个部分
|-数据域:存放数据的区域。例如:姓名、地址、学号etc...
|-指针域:存放一个指针的区域。这个指针
class Node {
private String name ; // 保留节点名称
private Node next ; // 保存下一个节点的引用
public Node(String name){ //含有参数的构造器来保存结点名称
this.name = name ;
//this.name中的name是Node类中的name
//= name 是传过来参数name
}
public String getName(){
return this.name ;
}
public void setNext(Node next){
this.next = next ;
}
public Node getNext(){
return this.next ;
}
}
public class Demo04 {
public static void main(String args[]){
Node na = new Node("A") ;
Node nb = new Node("B") ;
Node nc = new Node("C") ;
na.setNext(nb) ;//a的指针域指向b
nb.setNext(nc) ;//b的指针域指向c
}
}
在栈内存中存放的是na,nb,nc
在堆内存中存放的是name:A、B、C 和next:nb、nc、null
第一个结点A的指针域na指向的是nb,所以堆内存中存放的是结点A到的name和nb。同理可得。
在设置好了关系之后,只能通过递归的方式完成全部输出。
public class Demo05 {
public static void main(String args[]){
Node na = new Node("车厢 A") ;
Node nb = new Node("车厢 B") ;
Node nc = new Node("车厢 C") ;
na.setNext(nb) ;
nb.setNext(nc) ;
print(na) ;
}
public static void print(Node node){
if(node != null){ // 避免空指向异常
System.out.println(node.getName()) ;
if(node.getNext() != null){
print(node.getNext()) ;
}
}
}
}
自动的完成增加和输出的功能
需要考虑到以下的几个问题:
每一个结点都要保存起来。设置一个可以保存节点关系的类:Node
需要一个添加结点,并按照结点次序连接起来的类:Link
第一个结点是头结点,必须保留好根节点,private封装起来
下面是一个链表的基本数据模型。
class Node {
private String name ; // 保留节点名称
private Node next ; // 保存下一个节点的引用
public Node(String name){
this.name = name ;
}
public String getName(){
return this.name ;
}
public void setNext(Node next){
this.next = next ;
}
public Node getNext(){
return this.next ;
}
public void addNode(Node newNode){//添加结点
if(this.next == null){
this.next = newNode ;
} else {
this.next.addNode(newNode) ; // 当前节点的下一个继续往下判断
}
}
public void printNode(){//输出结点
System.out.println(this.name) ;
if(this.next != null){
this.next.printNode() ;
}
}
}
class Link { // 表示的是一个节点的操作类
private Node root ; // 表示根节点
public void add(String name){ // 增加新节点
Node newNode = new Node(name) ; // 新节点
if(this.root == null){ // 现在没有根节点
this.root = newNode ; // 第一个节点是根节点
} else { // 应该排在最后
this.root.addNode(newNode) ; // 向后面排队
}
}
public void print(){
this.root.printNode() ;//从根结点开始调用,这个函数的方法在类Node中
}
}
public class Test {
public static void main(String args[]){
Link link = new Link() ;
link.add("车厢 A") ;
link.add("车厢 B") ;
link.add("车厢 C") ;
link.add("车厢 D") ;
link.add("车厢 E") ;
link.print() ;
}
}