C++ 基础知识回顾(I/O)

[1] I/O基础

大多数计算机语言的输入输出的实现都是以语言本身为基础的,但是C/C++没有这样做。C语言最初把I/O留给了编译器实现人员。这样做的一个原因是可以提供足够的自由度,使之最适合目标机器的硬件条件。但是大多数实现人员都将I/O建立在了Unix库函数中,之后C才将该库引入了C标准中,被C++继承了下来。

但是C++也有自己的I/O解决方案,将其定义于iostream和fstream中。这两个库不是语言的组成部分,只是定义了一些类而已。

C++把输入和输出看作字节流。对于面向文本的程序,一个字节代表一个字符。流充当了源于目标之间的桥梁,因此对流的管理包含两个方面:从哪里来,到哪里去。其中,缓冲区作为临时存储空间,可以提高流处理的速度。

C++中专门定义了一些类来管理流和缓冲区:

C++98定义了一些模板类,用以支持char和wchar_t类型;

C++ 11添加了char16_t和char32_t类型(实则就是一些typedef来模拟不同的类型)。

C++程序中若包含了iostream类库,则自动创建8个流对象:4个窄的,4个宽的:

cin: 标准输入流,默认关联到键盘[与之对应的宽型为wcin(处理wchar_t)];

cout: 标准输出流,默认关联到显示器[与之对应的宽型为wcout(处理wchar_t)];

cerr: 标准错误输出流,默认关联到显示器,这个流没有缓冲区,意味着信息会直接发送到显示器[与之对应的宽型为wcerr(处理wchar_t)];

clog: 标准错误输出流,默认也关联到显示器,但是这个流对应着缓冲区[与之对应的宽型为wclog(处理wchar_t)]。

[1.1] cout 的使用

C++将输出看作是字节流(可能是8位/16位/32位),但在将字节流发送给屏幕时,希望每个字节表示一个字符值。比如,要显示-2.34,需要5个字符表示,分别为-/2/./3/4,并不是这个值的浮点数表示形式。因此,ostream类的最重要的任务之一就是将数值类型转换为文本形式表示的字符流。为了达到这个目标,C++使用了如下方法:

重载的<<运算符:C++的重载操作除了基本的内置类型,还有如下几种:const signed char*/const unsigned char*/const char*/void*。

对于其他类型的指针,C++将其看作void*,然后打印地址的数值。如果要想获得字符串的地址,必须将其强转为void*类型。

ostream类的put()和write()方法,前者用于显示字符,后者用于显示字符串。

ostream& put(char);

// cout.put(‘B’).put(‘T’);

basic_ostream<charT, traits>& write(const char_type* s, streamsize n);

// write()的第一个参数是要显示的字符串的地址,第二个参数是要显示字符个数。

 1 const char* name = "benxintuzi";
 2 for(int i = 0; i <= strlen(name); i++)
 3 {
 4     cout.write(name, i);
 5     cout << endl;
 6 }
 7
 8 /** output */
 9 b
10 be
11 ben
12 benx
13 benxi
14 benxin
15 benxint
16 benxintu
17 benxintuz
18 benxintuzi

说明:

write()方法不会在遇到空白字符就停止打印,而是打印指定数目的字符,即使超出了字符串的边界。write()也可将数字转换为相应的形式打印,此时需要将数字的地址转换为char*,但是不会打印字符,而是内存中表示数值的位,可能是乱码,这个再正常不过了。

由于ostream类对数据进行了缓冲,因此利用cout也许并不会立即得到输出,而是被存储在缓冲区中,直至缓冲区填满为止。通常,缓冲区大小为512字节及其整数倍。如果需要将尚未填满的缓冲区进行刷新操作,需要执行如下代码:

cout << flush; // 立即刷新

或者:

cout << endl;   // 立即刷新并插入换行符

控制数值显示的格式:

dec/hex/oct,虽然这些都是函数,但是其通常的使用方式是:

cout << hex;

cout << value << endl;

使用width成员函数将不同的字段放到宽度相同的字段中:有两个函数版本:

int width()/int width(int i);

第一个返回当前字段所占的空格数;

第二个函数将宽度设置为i个空格后,返回旧的空格数。但是注意:该函数只影响紧挨着它的一项显示输出,然后将字段宽度恢复为默认值。

说明:

C++不会截断数据,比如如果在宽度为2的字段中打印7位的值,那么该字段将被增大,C++的原则是:内容比形式更重要。

默认情况下,C++的对其方式为右对齐,并且默认的填充字符是空格。可以使用fill()成员函数来更改填充字符,例如:cout.fill(‘*’);

 1 string birth("19890608");
 2 cout.width(20);
 3 cout << birth << endl;
 4 cout.fill(‘*‘);
 5 cout.width(20);
 6 cout << birth << endl;
 7
 8 /** output */
 9             19890608
10 ************19890608

默认情况下,浮点数的显示精度指的是总位数;

在定点模式和科学模式下,指的是小数点后的位数;

C++默认的精度为6位,并且末尾的0不显示。可以使用cout.precision(int)设置精度的位数,一旦设置,直到第二次设置前永远有效。

对于有些输出,必须保留末尾的0,此时需要借助于ios_base类中的setf()函数:cout.setf(ios_base::showpoint);

为了简化格式设置工作,C++专门提供了一个工具(头文件<iomanip>),3个最常用的格式控制符分别为:setprecision()、setfill()、setw(),分别用来设置精度、填充字符、字段宽度。

 1 for(int i = 1; i <= 10; i++)
 2 {
 3 cout << setw(5) << i << setw(10) << i * 10 << endl;
 4 }
 5
 6 /** output */
 7     1        10
 8     2        20
 9     3        30
10     4        40
11     5        50
12     6        60
13     7        70
14     8        80
15     9        90
16    10       100

[1.2] cin 的使用

cin对象将标准输入表示为字节流,通常情况下,键盘生成的是字符流,cin根据接受值的变量的类型,将字符序列转换为所需的类型。

再次强调:C++解释输入的方式取决于变量的数据类型。istream类重载了>>,可以识别如下基本类型:

signed char&/unsigned char&/char&/short&/unsigned short&/int&/unsigned int&/long&/unsigned&/long long&/unsigned long long&/float&/double&/long double&。

除此之外,还支持:

signed char*/char*/unsigned char*。

[1.3] 流状态

cin和cout对象各自包含一个描述流状态的数据成员(从ios_base中继承的)。流状态被定义为iostate类型,实则是bitmask类型,3个ios_base元素组成:eofbit/badbit/failbit。其中每个元素都是一位:

当cin到达文件末尾时,置位eofbit;

当cin未能读取到预期的字符时,置位badbit;

当I/O故障失败时,置位failbit。

当全部三个状态位设置为0时,说明一切顺利。程序可以根据当前的流状态决定下一步的动作。

[1.4] 字符的输入

首先应该确定是否希望跳过空白符,希望跳过空白则选择>>;如果希望检查输入的每个字符,则使用get(void)或者get(char&)。如果要将标准C程序改写为C++程序,可以使用cin.get()替换getchar(), 使用cin.put(ch)替换putchar(ch)。

字符串输入函数getline()、get()、ignore():


istream& get(char*, int, char);

istream& get(char*, int);

istream& getline(char*, int, char);

istream& getline(char*, int);

其中:

char*: 存放字符串的内存单元地址;

int: 内存单元大小(比字符串长度大1,用于存放’\0’);

char: 指定分界符。

例如,将字符串读入字符数组line中:

char line[50];

cin.get(line, 50);

读取时,get将在到达第49个字符或者遇到换行符后停止从流中读取字符,其在中途若遇到流中的换行符,那么将其留在流中不予处理,下一次读取时,首先看到的仍然是换行符;而getline则会将遇到的换行符读取出来并丢弃掉,这样流中就没有换行符了。

cin.ignore(255, ‘\n’)表示即将读取接下来的255个字符并且丢弃掉或者遇到分界符\n后停止读取操作,其默认形式为:istream& ignore(int = 1, int = EOF)。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <iomanip>
 4 using namespace std;
 5
 6 const int Limit = 255;
 7
 8 int main()
 9 {
10     char input[Limit];
11
12     cout << "Enter a string for getline() processing: ";
13     cin.getline(input, Limit, ‘#‘);  // 丢弃流中的#
14
15     cout << "Here is your input: ";
16     cout << input << "\nDone with phase 1" << endl;
17
18     char ch;
19     cin.get(ch);                    // 读取到的字符为#后边的第一个字符
20     cout << "The next input character is: " << ch << endl;
21     if(ch != ‘\n‘)
22         cin.ignore(Limit, ‘\n‘);
23
24     cout << "Enter a string for get() processing: ";
25     cin.get(input, Limit, ‘#‘);     // 保存流中的#
26
27     cout << "Here is your input: ";
28     cout << input << "\nDone with phase 2" << endl;
29
30     cin.get(ch);                    // 读取到的字符为#
31     cout << "The next input character is: " << ch << endl;
32
33     return 0;
34 }
35
36 /** output */
37 Enter a string for getline() processing: I love tu#zi !
38 Here is your input: I love tu
39 Done with phase 1
40 The next input character is: z
41 Enter a string for get() processing: I love tu#zi !
42 Here is your input: I love tu
43 Done with phase 2
44 The next input character is: #

除了前面介绍的方法外,其他的istream方法还包括read()/peek()/gcount()/putback()等:


read()读取指定数目的字节并存储到指定的位置,如:

char gross[100];

cin.read(gross, 100);

说明:

与get和getline不同,read不会在输入后加上’\0’,因此,不能将输入转换为字符串。Read常与ostream write()函数结合使用,完成文件的输入和输出。返回类型为istream&。

peek()函数返回输入中的下一个字符,但是不去读下一个字符,也就是说可以用来作为检查流的作用,以此来判断是否还要继续读取:

char gross[80];

char ch;

int i = 0;

while((ch = cin.peek()) != ‘\n’)

cin.get(gross[i++]);

gross[i] = ‘\0’;

gcount()返回最后一个非格式化方法读取的字符个数,意味着get()/ getline()/ ignore()/ read()方法读取的字符个数。实际中用的不多,可以用strlen()代替。

putback()方法将一个字符退回到流中,以便下一次读取。该方法返回一个istream&。使用peek()的效果相当于先使用一个get()读取一个字符,然后使用putback()将该字符退回到流中去。当然,putback()也可将字符放置在流中的其他位置。

 1 #include <iostream>
 2 using namespace std;
 3
 4 int main()
 5 {
 6     char ch;
 7
 8     while(cin.get(ch))
 9     {
10         if(ch != ‘#‘)
11             cout << ch;
12         else
13         {
14             cin.putback(ch);
15             break;
16         }
17     }
18     if(!cin.eof())
19     {
20         cin.get(ch);
21         cout << endl << ch << "is the next input character." << endl;
22     }
23     else
24     {
25         cout << "End of file reached." << endl;
26     }
27
28     while(cin.peek() != ‘#‘)
29     {
30         cin.get(ch);
31         cout << ch;
32     }
33     if(!cin.eof())
34     {
35         cin.get(ch);
36         cout << endl << ch << "is the next input character." << endl;
37     }
38     else
39     {
40         cout << "End of file reached." << endl;
41     }
42
43     return 0;
44 }
45
46 /** output */
47 I love #tuzi and I am #benxin.
48 I love
49 #is the next input character.
50 tuzi and I am
51 #is the next input character.

[2] 文件I/O

文件类的I/O与基本I/O非常相似,其相关类派生自iostream类,只是要操作文件,还必须将文件与流进行关联起来。

[2.1] 文件I/O基础

要让程序写文件,必须进行如下操作:

1 创建一个ofstream对象管理输出流:ofstream fout;

2 将该对象与特定文件关联起来:fout.open(“jar.txt”)【如果jar.txt不存在,那么会自动创建它】;

3 将内容写入文件中去:fout << “benxintuzi test”;

要完成上述任务,首先包含头文件fstream, 由于fstream中的类大多派生自iostream中的类,因此,fstream必然包含了iostream,因此,包含头文件fstream后不必再显式包含iostream了。

说明:

可以在创建流的同时关联文件:ofstream fout(“jar.txt”);

以上述这种默认模式打开文件时,如果文件不存在,则自动创建该文件;若文件存在,则自动将文件清空,然后再写入新内容。

程序读文件流程类似:

ifstream fin;

fin.open("jayne.txt");

char buf[50];

fin.getline(buf, 50); // 读入一行内容

cout << buf << endl;

虽然程序结束时会自动关闭流,但是最好显式关闭,如下:

fout.close();

fin.close();

关闭流并不是删除流,只是将断开流与文件的关联性。这样就可以重新将该流关联到另一个文件中。

[2.2] 流状态检查

C++文件流类从ios_base类继承了一个表示流状态的成员以及报告流状态的方法。可以通过这些方法判断流操作是否成功。比如检查打开文件是否成功,可以如下:

fin.open(“***”);

if(fin.fail())

或者:

fin.open(“***”);

if(!fin)

或者利用C++提供的新方法:

if(!fin.is_open())

说明:

if(fin.fail())、if(!fin.good())、if(!fin)...是等价的。但是如果以不合适的文件模式打开文件失败时,这些方法将不会检测出来。相比之下,is_open()却可以检测到这种错误。

在打开多个文件时,常理来说需要为每个文件关联一个流对象,然而为了节约系统资源,可以创建一个流,然后分别关联到不同的文件中。

文件模式描述的是打开的文件将被如何使用:读、写、追加等。

ifstream fin(“jar.txt”, model);

ofstream fout;

fout.open(“jar.txt”, model);

ios_base类定义了一个openmode类型,用来表示模式。与fmtflags和iostate类型一样,它也是一个bitmask类型。可以选择ios_base类中定义的多个常量来指定模式:

说明:

ios_base::ate和ios_base::app都将文件指针指向打开的文件尾,二者的区别在于:ios_base::app模式只允许将数据添加到文件尾,而ios_base::ate模式将指针放到文件尾。

追加文件示例:

 1 #include <iostream>
 2 #include <fstream>
 3 #include <string>
 4 #include <cstdlib>
 5
 6 using namespace std;
 7
 8 const char* file = "append.txt";
 9
10 int main()
11 {
12     // show the initial contents
13     ifstream fin;
14     fin.open(file);
15     if(fin.is_open())
16     {
17         cout << "The contents of " << file << " is: " << endl;
18         char ch;
19         while(fin.get(ch))
20             cout << ch;
21         fin.close();
22     }
23
24     // add new contents to the end of file
25     ofstream fout(file, ios::out | ios::app);
26     if(!fout.is_open())
27     {
28         exit(EXIT_FAILURE);
29     }
30     else
31     {
32         cout << "Enter your contents to be added: ";
33         string contents;
34         while(getline(cin, contents) && contents.size() > 0)
35             fout << contents << endl;
36         fout.close();
37     }
38
39     // show revised file
40     fin.clear();
41     fin.open(file);
42     if(fin.is_open())
43     {
44         cout << "The contents of " << file << " is: " << endl;
45         char ch;
46         while(fin.get(ch))
47             cout << ch;
48         fin.close();
49     }
50     cout << endl << "Done !" << endl;
51
52     return 0;
53 }
54
55 /** output */
56 The contents of append.txt is:
57 This is line 1
58 This is line 2
59 This is line 3
60 Enter your contents to be added: I love tuzi
61 So you can guess
62
63 The contents of append.txt is:
64 This is line 1
65 This is line 2
66 This is line 3
67 I love tuzi
68 So you can guess
69
70 Done !

说明:

以二进制读写文件时,请使用write和read方式。

[2.3] 随机存取文件

随机存取常被用于数据库文件中,可以根据索引操作数据项。

在文件中移动指针:

seekg(): 将输入指针移到指定的位置;

seekp(): 将输出指针移到指定的位置。

说明:

其实由于fstream类使用缓冲区来存储中间数据,因此指针指向的是缓冲区的位置,而不是真正的文件中的位置。

检查文件指针的当前位置:

对于输入流,使用tellg();

对于输出流,使用tellp();

它们都返回一个表示当前位置距离起始位置的偏移量,单位为字节。

在系统中需要创建临时文件时,可以使用cstdio中的char* tmpnam(char* pszName)函数,该函数可以生成TMP_NAM个不同的临时文件名,其中每个文件名包含的字符不超过L_tmpnam个。

 1 #include <cstdio>
 2 #include <iostream>
 3
 4 using namespace std;
 5
 6 int main()
 7 {
 8     char pszName[L_tmpnam] = {‘\0‘};
 9     cout << "10 temp file names are follows: " << endl;
10     for(int i = 0; i < 10; i++)
11     {
12         tmpnam(pszName);
13         cout << pszName << " ";
14     }
15     cout << endl;
16
17     return 0;
18 }
19
20 /** output */
21 10 temp file names are follows:
22 \s4ic. \s4ic.1 \s4ic.2 \s4ic.3 \s4ic.4 \s4ic.5 \s4ic.6 \s4ic.7 \s4ic.8 \s4ic.9

[2.4] 内核格式化

iostream提供程序与终端之间的I/O;

fstream提供程序和文件之间的I/O;

sstream提供程序和string对象之间的I/O。

读取string对象中的格式化信息或者将格式化的信息写入到string对象中被称为内核的格式化(incore formatting):

sstream类定义了一个从ostream类派生而来的ostringstream类,当创建了一个ostringstream对象时,就可以将信息写入其中。该对象使用动态内存分配来增大缓冲区。ostringstream类有一个名为str()的成员函数,该函数返回一个被初始化为缓冲区内容的string对象。

string mesg = outstr.str();

注意:使用str()方法后不能再对ostringstream对象进行写操作。

与之相对的有istringstream类。

 1 #include <iostream>
 2 #include <sstream>
 3 #include <string>
 4 using namespace std;
 5
 6 int main()
 7 {
 8     ostringstream outstr;   // 管理一个string流
 9     string name;
10     cout << "What‘s the name of your hard disk ? : ";
11     getline(cin, name);
12     cout << "What‘s the capacity in GB ? : ";
13     int cap;
14     cin >> cap;
15
16     outstr << "The name is : " << name << ", and the capacity is " << cap << " GB." << endl;
17     string str = outstr.str();
18
19     // <11111>
20     cout << "<11111>" << endl;
21     cout << str << endl;
22
23     // <22222>
24     cout << "<22222>" << endl;
25     istringstream instr(str);
26     string word;
27     while(instr >> word) // read a word every a time
28         cout << word << endl;
29
30     return 0;
31 }
32
33 /** output */
34 What‘s the name of your hard disk ? : TOSHIBA
35 What‘s the capacity in GB ? : 320
36 <11111>
37 The name is : TOSHIBA, and the capacity is 320 GB.
38
39 <22222>
40 The
41 name
42 is
43 :
44 TOSHIBA,
45 and
46 the
47 capacity
48 is
49 320
50 GB.
时间: 2024-11-05 14:43:37

C++ 基础知识回顾(I/O)的相关文章

java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)

 *java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时候才能消费,仓空则等待. *3.当消费者发现仓储没有产品可消费的时候,会唤醒等待生产者生产. *4.生产者在生产出可以消费的产品的时候,应该通知等待的消费者去消费. 下面先介绍个简单的生产者消费者例子:本例只适用于两个线程,一个线程生产,一个线程负责消费. 生产一个资源,就得消费一个资源. 代码如下: pub

java基础知识回顾之java Thread类学习(七)--java多线程通信等待唤醒机制(wait和notify,notifyAll)

1.wait和notify,notifyAll: wait和notify,notifyAll是Object类方法,因为等待和唤醒必须是同一个锁,不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,所以可以被任意对象调用的方法,定义在Object基类中. wait()方法:对此对象调用wait方法导致本线程放弃对象锁,让线程处于冻结状态,进入等待线程的线程池当中.wait是指已经进入同步锁的线程,让自己暂时让出同步锁,以便使其他正在等待此锁的线程可以进入同步锁并运行,只有其它线程调用notify方

java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁

1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数封装的代码操作tickets,同步代码块中的锁我们可以指定.假设我们事先不知道同步函数用的是什么锁:如果在同步代码块中指定的某个锁(测试)和同步函数用的锁相同,就不会出现线程安全问题,如果锁不相同,就会发生线程安全问题. 看下面的代码:t1线程用的同步锁是obj,t2线程在操作同步函数的资源,假设不

JS基础知识回顾:引用类型(一)

在ECMAScript中引用类型是一种数据结构,用于将数据和功能组织在一起,而对象时引用类型的一个实例. 尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构,所以虽然说引用类型与类看起来想死,但他们并不是相同的概念. 不过引用类型有的时候也可以被称为对象定义,因为他们描述的是一类对象所具有的属性和方法. 新对象是使用new操作符后跟一个构造函数来实现的,构造函数本身就是一个函数,只不过该函数时处于创建新对象的目的而定义的. ECMASc

java基础知识回顾之javaIO类--管道流PipedOutputStream和PipedIutputStream

管道流(线程通信流):管道流的主要作用是可以进行两个线程间的通讯,分为管道输出流(PipedOutputStream).管道输入流(PipedInputStream),如果想要进行管道输出,则必须要把输出流连在输入流之上.如图所示: 1.管道输入流应该连接到管道输出流 ,输入流和输出流可以直接连接       2.使用多线程操作,结合线程进行操作.通常由某个线程从管道输入流中(PipedInputStream)对象读取.          并由其他线程将其写入到相应的端到输出流中.不能使用单线程

java基础知识回顾之java Thread类学习(五)--java多线程安全问题(锁)同步的前提

这里举个例子讲解,同步synchronized在什么地方加,以及同步的前提: * 1.必须要有两个以上的线程,才需要同步. * 2.必须是多个线程使用同一个锁. * 3.必须保证同步中只能有一个线程在运行,锁加在哪一块代码 那么我们要思考的地方有:1.知道我们写的哪些是多线程代码 2.明确共享数据 3.明确多线程运行的代码中哪些语句是操作共享数据的.. 4.要确保使用同一个锁. 下面的代码:需求:两个存户分别往银行存钱,每次村100块,分三次存完. class bank{ private int

java基础知识回顾之---java String final类普通方法

辞职了,最近一段时间在找工作,把在大二的时候学习java基础知识回顾下,拿出来跟大家分享,如果有问题,欢迎大家的指正. /*     * 按照面向对象的思想对字符串进行功能分类.     *      *      * 1,获取:     * 1.1 获取字符串中字符的个数(长度).     *         int length();     * 1.2 取字符串中的某一个字符,其中的参数index指的是字符串中序数.字符串的序数从0开始到length()-1 .     *       

python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。

本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding:utf-8from com.wenhy.crawler_baidu_baike import url_manager, html_downloader, html_parser, html_outputer print "爬虫百度百科调度入口" # 创建爬虫类class SpiderMai

C#学习笔记(基础知识回顾)之值类型与引用类型转换(装箱和拆箱)

一:值类型和引用类型的含义参考前一篇文章 C#学习笔记(基础知识回顾)之值类型和引用类型 1.1,C#数据类型分为在栈上分配内存的值类型和在托管堆上分配内存的引用类型.如果int只不过是栈上的一个4字节的值,该如何在它上面调用方法? 二:值类型转换为引用类型--装箱 2.1CLR对值类型进行装箱时:新分配托管堆内存,将值类型的实例字段拷贝到新分配的内存中,返回托管堆中新分配对象的地址.这个地址就是一个指向对象的引用. int i = 10; Object obj = i; 三:将引用类型转换为值

java基础知识回顾之---java String final类之intern方法

public class StringObjectDemo { /** * @param args */ public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " ");//true System.out.print((Other.hello == hel