Java自学入门|笔记实录
前言:本文章对于java的入门学习基于有C/C++以及python基础,部分重叠的基础知识可能有大量删减
Java简述
简单易学
Java最初是为对家用电器进行集成控制而设计的一种语言,因此它必须简单明了。
Java语言的简单性主要体现在四个方面:
1、Java的风格类似于C++,从某种意义上讲,Java语言是C及C++语言的一个变种因而C++程序员初次接触Java语言,就会感到很熟悉。
2、Java摒弃了C/C++中容易引发程序错误并且难以掌握的一些特性,如指针、结构、以及内存管理等。
3、Java提供了丰富的类库,可以帮助我们很方便的开发Java程序。
安全性高
1、java是一种强类型的语言,其类型检查比C/C++还要严格。类型检查帮助我们检查出许多开发早期出现的错误
2、java提供了垃圾回收机制,有效的避免了C/C++中最头疼的内存泄漏问题
3、java禁止非法内存访问,在没有授权的情况下是不能访问内存的.所有这些措施,使Java程序员不用再担心内存的崩溃
跨平台
Java作为一种网络语言,其源代码被编译成一种结构中立的中间文件格式。
只要有Java运行系统的机器都能执行这种中间代码。Java源程序被编译成一种与机器无关的字节码格式,在Java虚拟机上运行。
多线程
从略
快速开始
新建文本文件,命名为QuickStart.java,通过创建同名的 类,并调用System类中的out的println方法输出内容。
程序运行接口与C类似的,是main()函数.
1 | //QuickStart.java |
基本数据类型与运算
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
| 基本数据类型 | 占字节数 | 占位数 | 最小值 | 最大值 | 包装类型 | 默认值 |
|---|---|---|---|---|---|---|
boolean | 1字节 | 8位 | Boolean | false | ||
byte | 1字节 | 8位 | -128 | 127 | Byte | 0 |
char | 2字节 | 16位 | \u0000 | \uffff | Character | \u0000 |
short | 2字节 | 16位 | -2^15 | 2^15-1 | Short | 0 |
int | 4字节 | 32位 | -2^31 | 2^31-1 | Integer | 0 |
float | 4字节 | 32位 | Float | 0.0f | ||
long | 8字节 | 64位 | -2^63 | 2^63-1 | Long | 0L |
double | 8字节 | 64位 | Double | 0.0d |
- 与C++不同,Java中的布尔值是
boolean,而不是bool,可用%b或%B输出。 - 与C不同,输出时:整数都是
%d,浮点数都是%f,没有%lf等概念。 - 值得注意的是,java中,
char型数据占两个字节,编码不是ASCII码而是 Unicode码。
类型转换
数据类型的转换遵循以下规则:
不能对
boolean进行类型转换不能把类型转换成其它对象类型
把容量大的类型转换成容量小的类型,需通过比较 进行强制转换
强制类型转换可能会损失精度
$ 注:byte < char < int < long < float < double $
运算符
以下介绍均以展现异同为主,不过多介绍
1 | //加法运算 |
- JAVA中的加法在C的基础上扩充了许多功能,诸如字符串拼接等。(这与C++和python类似)
1 | //取余运算 |
JAVA中,取余运算符的右边可以为浮点数,而
/则与C中一致,若两边均为整型结果取整。但是python中,
/默认结果是浮点数,//才表示整除含义
1 | //位运算符 |
- 与C不同,C 只存在
>>和<<,实际效果因编译器而异;但java运用两种形式做了统一
流程控制
if语句、for循环、while循环、switch语句,break、continue、return用法……
与C一致(笑)
待补充
面向对象
类的创建
java 和 C++与python “如出一辙”,我们通过class这个关键字,创建一个类。
1 | class GirlFriend{ |
而在主函数中,通过这个 类 来创建一个对象:
1 | public static void main(String[] args){ |
- 当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。
与C\+\+不同的最根本的原因是JAVA的语法规则。
我们知道,new就是在 堆 中动态的分配一个空间,以上面的例子来看,也就是相当于将堆中的这块内存的地址传给 在 栈 中 静态分配的jane,用C的话来说,此时jane就是指向这个对象的一个 指针。
1 | GirlFriend* jane = new FirlFriend(); |
总而言之,在java中,我们可以简单的把jane就理解为一个GirlFriend对象,并对其操作。(但实际上二者并不等同,中间通过指针操作)
理解这一点后,我们再来看下面这段代码:
1 | GirlFriend jane = new GirlFriend(); |
重点看看我们标line1的那一行代码,如果我们简单的理解为 “重新创建一个新对象,对象中的所有属性值都与jane对象一模一样,二者是两样东西”,那就错了。
我们发现,在line2中,我们对jane的体重进行设置,最后输出robin的体重,却还是和jane一样。
这说明,我们的好兄弟找的女朋友并不是jane的双胞胎,而是jane换了一个名字robin去勾搭我们的好兄弟!
jane和robin这两个变量代表的,其实是同一个对象。
如果用C中指针的想法来思考,那就是这两个指针都指向同一个内容。
访问控制符
访问控制符一定程度上可以保证程序的安全性,灵活使用可以使得类的外部不能轻易使用 私有属性和方法
示例:
1 | class GirlFriend{ |
类的访问控制符有四种:
public、private、protected默认【即不加任何修饰符
default】
在一个类的内部,所有的成员可以相互访问,访问控制符是透明的;访问控制符是针对外部而言的
外部访问包括两种方式:
- 通过类名访问类内部的成员
- 通过类对象名访问类内部成员
这与C++是完全一致的,具体可以参考C++的学习笔记
既然protected外部也可以访问,那么这与public有什么区别呢?
这在之后 继承 和 包 的学习中,会着重提及~
构造函数
在python的学习中,我们可以对类进行初始化操作:
1 | class Person : |
简单理解,就是我们在 “主函数”中,创建一个类对象时,会有一个函数自动执行,如果需要参数,就在new的时候传递。
就如python中的__init__()函数。
当然,作为面向对象的编程语言,C++也有 构造函数 的定义:
1 | class Person{ |
- 不难发现,C++中的许多内容,包括访问控制符都在java中得到体现,所以 构造函数也同样得到继承
在Java中,构造函数同样需要和类名相同,不需传参时,也不必像python一样需要加入self。
继续以之前的女朋友为例,编写可初始化身高和体重属性的构造函数,如下:
1 | class GirlFriend{ |
规则总结:
- “构造函数”不能有 “返回值”
- 函数名 必须 与 类名 一致
事实上,在我们没有自己编写的构造函数之前,其实本身内部就相当于已经有一个无参数的空函数了:
1 | public GrilFriend(void){} |
但是,如果我们编写了一个构造函数之后,原来的空函数被替换掉了。
再一个问题,我们能否写多个不同的构造函数?
1 | public GrilFriend(void){ |
其实是可以的,这涉及到 函数重载 问题,事实上这是与C++一模一样的,具体可见:C++快速入门
this指针
在说明this指针之前,我们先来看一段C语言代码:
1 |
|
- 本代码中,
show_i()函数的作用是:将数据类型为struct A的指针所指向的i成员的数值打印出来 - 其中我们用到了一个形参变量:
THIS,即本质上,THIS是一个指针变量,从而将我们传给他的那个指针所指向的i找出来
在Java中,设计一个类模板之后,在“主函数”里创建多个示例时,其“方法成员”是存储在代码区/方法区,且只需储存一个的。就类似于上面的show_i()一样,只定义这一个,但是我却可以创给它不同的指针,它就给出不同的结果。
如:
1 | class A{ |
我们在“主函数”里,用不同实例如aa1,aa2来调用A.show_name()时,打印结果分别是aa1,aa2的不同的name。
那是因为,实际上在line1这一行中,show_name()的完整思路是show_name(A* this),
然后line2里面是println("name = " + this->name)。不过这些已经被java隐去了,程序员可以不去在意这背后的逻辑关系。
但是一般的情况下中,善用this->是更加符合代码规范的。
就如上面的println("name = " + this.name)一样,因此line3的语句也常常用下面的代码代替:this.name = j;
A:为什么
line2的规范写法不是用this->name,而是this.name?Q:这在我们前面的阐述中其实已经提到过了传送门,java隐去了指针的概念,所以与指针关联的两个符号:
*和->也没有了。
这其实就类似于我们python中的self关键词。
现在我们来回看一下前面讲 “构造函数” 的时候,我们搬出来的python的例子:
1 | class Person : |
可见,line4与前面的line3几乎是一样的。那么我可不可以像python一样,不再用j传递,也用名为name的形参进行传递呢?
答案是:可以。
因此,我们java的构造函数也能得到类似的代码:
1 | class A{ |
关于static
在前面
女朋友的例子中,我们利用指针的知识系统的阐述了jane和robin指向的是同一个对象。因此,如果对jane的身高进行修改,robin也会相应的改变(毕竟就是同一个东西)。
事实上,也确实可以将某一个属性设置为 静态变量,可将其视为 类本身的属性,而不只是对象的属性,属于公共的。这需要用到关键词static.
1 | //文件名:M.java |
【重要】总结:
- 多个对象可共用static属性
- 静态属性可以通过类名直接访问
- 想要访问静态属性还需该属性是非私有的(即:不被private控制符修饰)
- 静态方法不能访问非静态属性,但是非静态能访问静态
- 解释:因为在没有创建对象的情况下,不存在非静态的属性,因此无法访问
- static不能用来修饰构造函数
- 静态方法不能使用this和super(super会在之后的讲解中出现)
static的应用
【1】造轮子计数
1 | class A{ |
【2】出品限量款
1 | class A{ |
getOneA()必须是static,因为主函数无法new对象(因为构造函数是private),所以在主函数需要用类名来调用此函数- 属性
aa必须是static,否则getOneA()静态函数不能访问非静态成员,而且由于aa是static,所以创建成功后有且仅指向唯一一个,满足我们 只能创建一个对象 这样的需求
继承
之前我们的女朋友示例中,我们定义了许多属性,如:身高、体重、三围、男朋友的名字等等。我们发现,除了男朋友的名字这一项以外,其他的都是一个人类也都具备的属性。
事实上,为了代码的可读性和完善性,我们希望事先创建一个人类这样的类,然后再创建一个女朋友的类,然后女朋友这个类也有人类该有的一些属性。为此,继承这一要素成了必要。
简介
一个新类从已有的类那里获得其已有的属性和方法,这就叫类的继承。
这个新类被称为子类,也叫派生类,已有的那个类叫做父类,也叫做基类继承的好处
- 代码得到极大的重用
- 形成一种类的层次体系结构
- 为多态创造条件
如何继承
1 | class SuperClass{ |
- superclass表示父类,subclass表示子类。通过
extend来实现继承
注意事项
子类不能够继承私有(private)属性和方法
- 事实上私有属性也被继承过来了,可以通过我们接下来的super语句进行初始化。
- 但是普通的逻辑上语法上是无法直接在子类里面访问的。因此继承必须慎重,否则会浪费内存
继承/包 限制 表
| 作用域 | 当前类 | 同一 package | 子孙类 | 其他 package |
|---|---|---|---|---|
| public | √ | √ | √ | √ |
| protected | √ | √ | √ | × |
| friendly | √ | √ | × | × |
| private | √ | × | × | × |
- Java只支持 单继承,不支持多继承
- C++可以通过虚继承实现多继承,而Java可以通过 接口 一定程度上解决多继承的部分需求
- 子类不能继承父类的构造方法
- 可以通过
super部分解决
- 可以通过
子类构造函数
我们知道,构造函数可以很方便的为类成员进行初始化
1 | class A{ |
而当有一个新的类继承上面的类之后,其本身也会继承有相应的属性
1 | class B extends A{ |
但是想要也对继承过来的属性进行初始化就得
1 | public B(int i,int j,int k){ |
如果父类的属性较多,子类想要初始化属性的话,写构造函数也会十分繁琐。
在C++中,我们可以通过以下方式解决:
1 | /* B 的构造函数 */ |
不过这并不能在java中适用,因此Java引出了super的概念,只需在初始化B的时候调用super()函数,就完成了“调用父类A的构造函数完成初始化”这一行为:
1 | public B(int i,int j,int k){ |
但是,值得注意的是 :
super必须是在父类有构造函数的前提下,在子类的构造函数中调用,且必须是第一个执行语句
如果我们不在子类的构造函数中主动写
super语句,系统默认为其添加一个super()语句[super空]。- 如果此时父类中没有
无参版的构造函数,程序仍然报错
- 如果此时父类中没有
子类的构造函数中,能且只能使用一次
super()函数
举例:
1 | //test1 |
重写方法
有些时候,子类不一定要用到和父类一模一样的方法。这种时候我们可以对子类继承过来的方法进行“重写”:
1 | class A{ |
B中的f()方法和A的同返回值同形参列表,但是结果不再一样。
但是,重写也得有一定的规则限制:
- 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回类型
- 子类中不允许出现与父类同名同参但不同返回类型的方法,如果出现,编译时会报错
- 覆盖方法时,不能使用比父类中被覆盖的方法更严格的访问权限(原因我们会在多态的学习中再讨论)
| 父类使用 | 子类可用 |
|---|---|
| public | public |
| protected | public、protected |
| private | 不被继承 |
事实上,重写之后,我们还可以用回A的方法:
1 | class B extends A{ |
实例展示
1 | //M.java |



