第2章 从C过渡到Java
2.0 本章方法学导引
2.1 快速入门
2.1.1 Java类型概述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6SqhAZWZ-1632155034179)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210905152225954.png)]
引用型只能按名引用(类似更换"值日生"袖标,贴在张三、李四上),不能偏移(C指针可偏移,即指针值++、–)。
另外,引用型必须要关联一个==被引用的对象,==引用才有意义。
2.1.2 输出英文希腊字母、九九乘法表
class W2{//功能:打印英文字母、希腊字母、九九乘法表
//类W2只有两个成员(即两个函数/方法)
int x=5;
void prnEnChar(){//字面量
for(char i='a';i<='z';i++)
//System.out.print(i+'');//打印出一组数字,是因为:x+""==>产生字符串的连接,如'a'+" "==>"a" 'a'+' '==>Unicode字符'a'(无符号整数)+''(无符号整数)==>新的无符号整数
System.out.print(i+" ");//"123"+"abc"结果为123abc
x=x+5; System.out.print("\n x="+x+"\n ");//测试属性是所有方法的全局变量
}
void prnjj(){
for(i=1;i<=9;i++){
for(int j=1;j<=i;j++)
System.out.printf("%d*%d=%2d ",i,j,i*j);
System.out.printf("\n");//System.out.println();//ln表示line
}
}
}
class App{
public static void main(String[] args){
W2 w=new W2();//调用函数,必须先造对象
w.prnEnChar()
}
}
//1.通过上述编程,发现C的语法几乎都能直接使用,例外:打印System.out、main、所有东西都必须包在class里面;【【【重要:必须先造对象,再调用其方法
2.2.2 入门示例l:累加求合
与C程序相比,Java程序有一些不同
-
***类是Java程序的基本结构。***类的基本框架为:class 类名{类体},类体由一组变量、函数组成,均称作类的成员,其中变量也称作属性,函数也称作方法/行为。如Ch_2_1有3个成员:
成员变量
maxN,成员方法
sum()和main()。#成员变量和局部变量的作用域不同。
成员变量定义在类中、方法外,相当于方法的外部变量,故作用域是整个类,可直接应用于该类的成员方法。注:因main前有static修饰,故不可直接使用maxN。
#成员方法:函数的返回类型需于返回值保持一致。
-
***组成标识符的字母范围更广。***标识符就是程序员为变量/常量、函数、类等起的名字。Java 标识符由字母、数字、下划线、货币符等字符组成,数量不限,其中数字不可作为第一个字符。
关键字
是语言中有特殊意义的单词,不能用作标识符。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d7CTugH3-1632155034181)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210905180448130.png)]
-
输出语句不同。
System.out.printf(…)
-
***调用函数的方式不同。***必须先基于类创建对象,然后基于对象调用函数。例如:
Ch_2_1 c=new Ch_2_1();//用new 类名()创造对象
c.sum(10);//用 对象名.函数名(实参列表); 的方式调用函数。
-
main() 的书写方式不同。
main()依旧是执行程序的入口,但书写格式固定:public static void main(String[] x){…}
2.1.3 理解类和对象
2.1.4 顺序表及其应用
-
-
认识Java的数组类型
(1)
Java用方括号[]来标记数组
,例如:int [] a,b;//a、b是int[]型变量。==注:定义时不能指定容量,==如int[3]a;会报错。
(2)
必须先创建数组对象,才能引用其元素,
如:a=new int[10]; //创建数组a引用的对象
a[0]=5; //给数组元素赋值
b[0]=3; //编译错,因为b并未关联数组对象,b[0]不存在。
(3)
可以在定义时创建或初始化数组对象
,如:int [] c=new int [20];
int [] d={1,2,3,4,5}
(4)
每个数组对象都有一个常量属性length,用于记录数组的容量。
如:a.length、d.length的值分别是10、5. -
理解输入类Scanner
(1)使用前必须导入Scanner类,即在文件头部增加语句:import java.until.Scanner;
(2) 创建Scanner对象方式为: Scanner sc=new Scanner(System.in);
System.in代表标准输入设备,即键盘。这样通过键盘的所有输入都可从读取。数据间默认用空格分隔。
(3)读取int型数据的方式为 : int x=sc.nextInt();
说明:类似地,nextDouble()、nextBoolean()可以读取double、boolean型数据。
-
构造和使用顺序表
从构造角度看,顺序表=数组+表长+操作集,其中数组提供顺序存储空间,表长框定数组存储数据的范围,至于操作集,可根据需要灵活定义。
2.1.4.1 顺序表示例
【例2.3】设计存储int型元素的顺序表类Seqlist。要求:顺序表的最大容量在创建顺序表对象时指定;包含从尾部追加、输出表中全部数据、插入元素至指定位置、排序等基本操作。
import java.util.Scanner;//导入Scanner类,旨在告诉编译器:Scanner的位置 //换言之,在本文件的其他地方,可以直接写Scanner,而不是写其全名:java.util.Scanner class Seqlist{//顺序表=数组+表长+操作集 数组有容量,相当于有容量,表长即教室的实际人数 int [] a=new int [100];//定义数组,a的类型是int[].java中的数组对象是new出来的 int len;//成员变量虽然没有赋值,但在对象构造时,均自动赋予默认值 //数值型默认值均为0,引用型均为null,boolean默认值均为false //猜测所有类均有构造函数,(自己定义或编译系统自动给出。而构造函数的作用是初始化) //注意:局部变量没有默认值 //新需求:要求在顺序表构造时之doing容量 Seqlist(int max){a=new int[max];}//注意,用的是方括号,即未调用构造函数 //即new int [max]造的是数组对象,该对象内有max个元素空间 void append(int x){//向数组尾部追加元素 1 2 3 4 a[len]=x;len++;//修改表长 } void insert(int x,int pos){//在pos处插入x for(int j=len-1;j>=pos;j--) a[j+1]=a[j]; a[pos]=x; len++; } void show(){//输出顺序表中的所有元素 //void show(Seqlist *L) for(int i=0;i<len;i++) System.out.print(a[i]+" "); }//前万不能孤立的看一个方法,方法执行时,还要考虑其调用者,如main中的s.show、s1、show,调用者不同,调用者内部嵌入的数组也就不同。 void sort(){ java.util.Arrays.sort(a,0,len); } } class App{ public static void main(String[] args){ Seqlist s=new Seqlist(100); Seqlist s1=new Seqlist(100); Scanner sc=new Scanner(System.in);//其中System.in代表标准输入设备--键盘 int x;System.out.println("请输入一组数,以0结束:"); x=sc.nextln();//从SC中读取一个int型数据 while(x!=0){s.append(x);x=sc.nextlnt();} //for(int i=0;i<10;i++)s.append(i); s.show(); System.out.printIn(); System.out.println("向s中0、2、5处插入11、22、99"); s.insert(11,0);s.show();System.out.printIn(); s.insert(22,2);s.show();System.out.printIn(); s.insert(99,5);s.show();System.out.printIn(); s.sort(); System.out.printIn(); s.show(); //System.out.printIn("S1=");s1.show();System.out.println("##"); //System.out.printIn(); } }
小结:注意show()、append()无参,输入的数据直接存于数组a中(前提:a必须new出对象)。
-
插入三步走:第一步:先移出空位(从哪里开始,到哪里结束,如何移,移动哪些元素),第二步:实施插入a[pos]=x,第三步:修改表长:len++。
排序:java.util.Arrays.sort(a,0,len);
2.1.4.2 顺序表合并
【例2.4】在例2.3的基础上,实现将两个升序表合并成新的升序表。
分析:涉及到两个对象的操作。合并:创建新表,合并到新表中,尾表的处理
-
import java.util.Scanner; class Seqlist{ int [] a=new int [100];int len; Seqlist(int max){a=new int[max];} void append(int x){a[len]=x;len++;} void show(){for(int i=0;i<len;i++)System.out.print(a[i]+" ");} void create(){ Scanner sc=new Scanner(System.in); int x;System.out.println("请输入一组数,以0结束"); x=sc.nextInt(); while(x!=0){append(x);x=sc.nextlnt();}//注意:调用自己的方法append(); } Seqlist merge(Seqlist L1,Seqlist L2){//升序表L1+升序表L2==》升序表返回 Seqlist L=new Seqlist(100); int i,j,k;i=0;j=0;k=0; while(i<L1.len&&j<L2.len)//实施两个表的比较,小者放入新表 if(L1.a[i]<L2.a[j]){ L.a[k]=L1.a[i];i++;k++;} else{L.a[k]=L2.a[j];j++;k++;} while(i<L1.len){L.a[k]=L1.a[i];i++;k++;} while(j<L2.len){L.a[k]=L1.a[j];j++;k++;} L.len=L1.len+L2.len; return L; } } class App{ public static void main(String[] args){ Seqlist s1=new Seqlist(100);Seqlist s2=new Seqlist(100); s1.create();s2.create(); System.out.println("S1:");s1.show(); System.out.print("\n"); System.out.println("S2:");s2.show(); System.out.print("\n"); Seqlist s3=s1.merge(s1,s2); System.out.println("S3:");s3.show(); System.out.print("\n"); } }
2.1.5 单链表及其应用类
2.1.5.1
单链表=数据域+next指针+操作集。
所有引用成员都有默认值,可以省略。
头结点:指向线性表中第一个元素的第一个元素的结点;
头指针:指向线性表的第一个元素的指针。
import java.util.Scanner;
class LinkedList{
int data;LinkedList next;
LinkedList(int x){ data=x;}//next=null;
//LinkedList(){;}同名不同参,即函数重载
void create(){
Scanner sc= new Scanner(System.in);
int x;
LinkedList tail,q;
System.out.print("input: ");
x=sc=nextInt();tail=this;//h.create()就是h,即头结点
while(x!=0){//造结点、把结点插入表尾、修改表尾、再读一个数据
q=new LinkedList(x);
tail.next=q;
tail=q;
x=sc.nextInt();
}
void show(){
LinkedList p;
for(p=this.next;p!=null;p=p.next)
System.out.print(p.data+" ");
}
void delAll(int x){//删除链表中所有值为x元素
//双指针移动
}
}
class App{
public static void main(String[] args){
LinkedList h=new LinkedList(0);
h.create();h.show();
}
}
注:没写构造函数,系统会给你补一个无参的构造函数,一旦写了,系统将不会给你补构造参数。//方法一:LinkedList(0);方法二:LinkedList(){;}同名不同参,即函数重载。
2.2 强化实践
2.2.1 二叉树及其应用
import java.util.Scanner;
//按需造类
class ReadChar{//专门用于给建树提供getchar()操作
String data;//存储建树用的字符串
int pos=-1;//指向当前应读的字符
ReadChar(String x){data=x;}
char getChar(){pos++;return data.charAt(pos);}//不能return data[pos];String的方法CharAt
}
class BTree{
char data;BTree L,R;
BTree(char x){data=x;}
BTree(){;}
//BTree create(ReadChar r){
static BTree create(ReadChar r){//静态方法的特色:可不造对象,直接通过类名使用
char x=r.getchar();
if(x=="#")return null;
BTree t=new BTree(x);
t.L=create(r);t.R=create(r);
return t;
}
void pre(){//C的方式pre(t);==>Java方式:t.pre();
//this一定不为空,this代表对象this=t;
System.out.print(this.data+" ");
//this.L.pre.();this.R.pre();//注:此处可能发生空指针引用,因为this.L或this.R可能为空
if(this.L!=null)L.pre();
if(this.R!=null)R.pre();
}
class Stack{//内部类,实际上是类中作为类型的成员
BTree[]data=new BTree[20];
int top;
void init(){top=0;}
boolean isEmpty(){return top==0;}
void push(BTree x){data[x]=x;top++}
BTree pop(){top--;return data[top--]; }
}
void preN(){
class Stack{}//可以,类,本质上就是类型,Stack定义在preN()中,即其作用域仅限于preN()
Stack st=new Stack();
BTree t=this;
while(t!=null||st.isEmpty()==false){
if(t!=null){
System.out.print(this.data+" ");//此句有误
System.out.print(t.data+" ");
st.push(t);t=t.L;}
else{
t=st.pop();t=t.R;
}
}
}
void inN(){
class Stack{}//可以,类,本质上就是类型,Stack定义在preN()中,即其作用域仅限于preN()
Stack st=new Stack();
BTree t=this;
while(t!=null||st.isEmpty()==false){
if(t!=null){
System.out.print(this.data+" ");//此句有误
//System.out.print(t.data+" ");
st.push(t);t=t.L;
}
else{
t=st.pop(); System.out.print(t.data+" ");t=t.R;
}
}
}
void in(){
if(this.L!=null)L.in();
System.out.print(this.data+" ");
if(this.R!=null)R.in();
}
void post(){
if(this.L!=null)L.post();
if(this.R!=null)R.post();
System.out.print(this.data+" ");
}
}
class App{
public static void main(String[] args);
// BTree t=new BTree();
Scanner r=new Scanner(System.in)
System.out.print("请输入建树数据,#表示null:\n")
String x=sc.nextLine();//next读到空格时停下
ReadChar r=new ReadChar(x);
//t=t.create(r);//实际上,t原来的对象就被扔掉了
BTree t=BTree.create(r);
System.out.print("\n pre: ");t.pre();
System.out.print("\n preN: ");t.preN();
System.out.print("\n in: ");t.in();
System.out.print("\n inN: ");t.inN();
System.out.print("\n post: ");t.post();
}
//建树即造根,通过造一个类来输入字符。
//二叉树的前序遍历非递归算法,进入左子树前需保存当前结点供遍历右子树之用。关键:何时保存、何时用、何时删除回溯点。
/*preOrder(btree *t){
initStack(S);
while(t不空||栈不空)
{
if(t不空){
访问t;//访问根
保留回溯点;
t=t->L;//访问左子树
}
else{
t=回溯点;
删除回溯点;
t=t->R;//访问右子树
}
}
}*/
preOrder(btree *t){
Stack S;
initStack(&S);
while(t!=NULL||栈不空)
{
if(t!=NULL){
V(t);
push(&S,t);t=t->L;
}
else{
t=pop(&S);
t=t->R;
}
}
}