Java 学习笔记(2)——基本语句、控制结构

上一篇中简单谈了一下本身对Java的一些见解并起了一个头,如今继续总结java的相关语法。java语法整体上与C/C++同样,因此对于一个C/C++程序员来讲,天生就能看懂Java代码。在学习java的时候,上手很是快。我感受本身就是这样,看代码,了解其中一些重点或者易错点的时候发现,与C/C++里面基本相似,甚至不少东西不用刻意去记,好像本身自己就知道坑在哪。因此这里我想简单列举一下语法点,而后尝试用C/C++的视角来解读这些特性。
java

引用类型

引用中的指针与内存

上一次,我总结一下java中的数据类型,在里面提到,Java中有两大数据类型,分为基本数据类型和引用数据类型。而且说明了简单数据类型,此次就从引用数据类型提及;
引用数据类型通常有:数组、字符串、类对象、接口、lambda表达式;此次主要经过数组和字符串来讲明它程序员

引用数据类型在C/C++中对应指针或者引用。其实关注过我以前C/C++反汇编系列文章的朋友知道,在C/C++中引用实质上就是一个指针。因此这里我也将java中引用类型理解为指针。因此从本质上讲,引用类型都是分配在堆上的动态内存。而后使用一个指针指向这块内存。这是理解引用类型很重要的一点。编程

数组的定义以下数组

char[] a = new char[10];
char[] b = new char[] {'a', 'b', 'c'};
char[] c = {'a', 'b', 'c'};

其实这种形式更符合 变量类型 变量名 = 变量值 这种语法结构, char[] 就像是一种数据类型同样,char表示数组中元素类型, []表示这是一个数组类型
字符串的简单定义以下:安全

String s = "Hello world";

上面说到引用类型都是分配在堆上的。因此字符串和数组实质上都是new 出来的。即便有的写法上并无new 这个关键字,可是虚拟机仍是帮助咱们进行了new 操做。有new就必定有delete了。那么哪里会delete呢?这些操做通常都由java的垃圾回收器来处理。咱们只管分配。这就帮助程序员从资源回收的工做中解放出来了。这也是java相比于C/C++来讲比较优秀的地方。有人可能会说C++中有智能指针,也有垃圾回收机制。确实是这样。可是我以为仍是有点不同。java是天生就支持垃圾回收,就好像从娘胎生出来就有这个本能,而C/C++是由后天学会的,或者说要刻意的去进行操做。两者仍是不同的。数据结构

下面有这样一段代码:函数

char[] a = new char[10];
System.out.println(a);

咱们打印这个a变量,发现它出现的是一个相似16进制数的一个东西。这个东西实际上是一个地址的hash值,为何不用原始值呢?我估计是由于有大神可以根据变量的内存地址进行逆向破解,因此这里为了安全对地址值进行了一个加密。或者为了完全贯彻Java不操做内存的信念。(我这个推断不知道是否是真的,若是有误,请评论区大牛指正。)这也就证实了我以前说的,引用类型本质上是一个指针。学习

char[] a = new char[]{'a', 'b', 'c'};
char[] b = new char[]{'a', 'b', 'c'};
System.out.println(a);
System.out.println(b);

上面这段代码,我想学过C/C++的人应该一眼就能看出,这里打印出来的a和b应该是不一样的值,这里建立了两块内存。只是内存中放的东西是同样的。大数据

char[] a = new char[]{'a', 'b', 'c'};
char[] b = a;
b[0] = '0';
b[1] = '1';
b[2] = '2';
System.out.println(a);
System.out.println(b);

这里从C/C++的角度来看,也很容易理解:定义了两个引用类型的变量,a、b都指向同一块内存,无论经过a仍是b来寻址并写内存,下次经过a、b访问对应内存的时候确定会发现值与最早定义的不一样。加密

String s = "hello";
System.out.println(s.hashCode());
s += "world";
System.out.println(s.hashCode());

因为Java不具有直接访问内存的能力,不能直接打印出它的内存地址,因此这里用hashCode 获得地址的hash值。经过打印结果说明这个时候s指向的地址已经变了。也就是说虽然能够实现字符串的拼接,可是虚拟机在计算得出拼接的结果后又分配了一块内存用来保存新的值。可是任然用s这个变量来存储地址值,用赵本山的话来讲就是“大爷仍是那个大爷,大妈已经不是原来的那个大妈了”。

也就是说Java分配内存的时候应该是按需分配,须要多少分配多少。不够就回收以前的,再从新按需分配。这就致使了java中字符串和数组的长度是不能改变的。

String s = "hello";
System.out.println(s.hashCode());
(s.toCharArray())[0] = 'H';
System.out.println(s.hashCode());
System.out.println(s); // 这里字符串的值不变

上述这段代码,经过toCharArray将字符串转化为char类型的数组,而后修改数组中的某一个元素的值,我原来觉得这样作至关于在String所在内存中修改,最终打印s时会出现 "Hello" ,可是从结果上来看并无出现这样的状况,s指向的地址确实没变,可是s也是没变的,那只能解释为toCharArray 又开辟了一块内存,将String中的值一一复制到数组中。

在学习中我尝试过各类数据类型强转

String s = "Hello World";
char[] a = (char[]) s;
int p = (int)s;

像这样的代码我发现并不能经过编译。在C/C++中,能够进行任意类型到整型或者指针类型的转化,常见的转化方式就是将变量所在地址进行赋值或者将变量对应的前四个字节进行转化做为int或者指针类型。可是在java中这点好像行不通。Java中强转好像只能在基本数据类型中实现,而在引用类型中一般由函数完成,而且完成时并非简单的赋值,还涉及到新内存空间的分配问题。

越界访问

因为C/C++中提供了访问内存的能力,并且因为现代计算机的结构问题,C/C++中存在越界访问的问题,越界访问能够带来程序编写的灵活性,可是也带来的一些安全隐患。对于灵活性,相信学习过Windows或者Linux编程的朋友应该深有体会,系统许多数据结构的定义常常有这类:

struct s {
    char c;
}

s *p = (s*)new char[100];

这样就简单的建立了一个字符串的结构。这里C变量只是提供了一个地址值,后续能够根据c的地址来访问它后续的内存。

安全问题就是大名鼎鼎的缓冲区溢出漏洞,我在相关博客中也详细谈到了缓冲区溢出漏洞的危害以及基本的利用方式。这里就不在赘述。

那么Java中针对这种问题是如何处理的呢? Java中因为不具备内存访问的能力,因此这里它简单记录当前对象的长度,只要访问超过这个长度,立马就会报异常,报一个越界访问的异常。(这里我暂时没有想到对应的java演示代码,因此简单说一下吧)

空指针访问

还记得C/C++指针中常见的一个NULL吧,既然Java中引用类型至关于一个指针,那么它确定也存在空指针问题。在Java中空指针定义为null。若是直接访问null引用,通常会报空指针访问异常。

char[] c = null;
c[0] = 'A'; //异常

语句

关于引用类型我暂时了解了这么多东西。下面简单列举一下java中的运算符和相关语句结构

运算符

java中的运算符主要有下列几个:

  1. 算数运算符: + 、-、 *、 /、 %、 ++、 --、
  2. 赋值运算符: = 、+=、 -=、%=、/=、*=
  3. 比较运算符: ==、 >、 <、 >=、 <=、 !=
  4. 逻辑运算符: &&、 ||、 !、
  5. 三目运算符
  6. 位运算符: >>、 <<、 >>>(无符号右移)、 <<<、&、|、~

这些运算符用法、要点、执行顺序与C/C++中彻底相同。因此这里简单列举。不作任何说明

语句结构

java中的顺序结构与其余语言中同样,主要有3种

  1. 顺序结构
  2. 判断结构: if、if...else、 if...else if...else if...else
  3. 循环结构:while、for、do while

用于与其余语言同样,这里须要注意的是,Java中须要判断的地方只能使用bool值做为判断条件,好比 5 == 3a == 5这样的。

在C/C++中有一条编程规范,像判断语句中将常量写在前面就像这样

if(5 == a){
    //....
}

这样主要是为了防止将 == 误写成 = ,由于在C/C++中 只要表达式的值不为0 就是真,像 if(a = 5) 这样的条件是恒成立的。而Java中规定判断条件必须是真或者假,而且规定boolean类型不能转化为其余类型,其余类型也不能转化为boolean,因此 if(a = 5) 这样的语句在Java中编译是不会经过的。

相关文章
相关标签/搜索