文章目录
一. 引言
1. 计算机程序
计算机本身是一个存储数据(数字、单词、图片)并与设备(监视器、音响系统、打印机)交互以及执行程序的机器
计算机程序(Computer Program):是一个指令和判断序列
计算机程序会详细的告诉计算机完成一个任务所需的步骤序列
2. 编程
编程:是设计和实现计算机程序的行为
算法:无歧义、可执行而且将终止的一个步骤序列
伪代码:是对解决问题的步骤序列的非正式描述
3. Java编程语言
要编写一个计算机程序,你需要提供CPU能够执行的一个指令序列
使用高级语言时,你只需要指定程序应当完成的动作,由编译器(compiler)将这些高级语言指令翻译成CPU所需的更详细的指令(机器指令)
4. Java发展历程
1995年5月23日,Java语言诞生;
1996年1月,第一个JDK-JDK1.0诞生;
1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入Java技术;
1996年9月,约8.3万个网页应用了Java技术来制作;
1997年2月18日,JDK1.1发布;
1997年4月2日,JavaOne会议召开,参与者逾一万人,创当时全球同类会议纪录;
1997年9月,JavaDeveloperConnection社区成员超过十万;
1998年2月,JDK1.1被下载超过2,000,000次;
1998年12月8日,Java 2企业平台J2EE发布;
2001年6月5日,Nokia宣布到2003年将出售1亿部支持Java的手机;
2001年9月24日,J2EE1.3发布;
2002年2月26日,J2SE1.4发布,此后Java的计算能力有了大幅提升;
2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE1.5更名为Java SE 5.0;
2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名,以取消其中的数字“2”:
J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME;
2009年12月,SUN公司发布Java EE 6;
2010年11月,由于甲骨文对Java社区的不友善,因此Apache扬言将退出JCP;
2011年7月28日,甲骨文发布Java SE 7;
2014年3月18日,甲骨文发表Java SE 8。
5. Java从编写到运行
(1). 启动Java开发环境
(2). 在编辑器上编写Java程序
(3). 运行Java程序
为了运行程序,Java编译器把源文件(source file)转换为类文件(class file),类文件包含面向对象Java虚拟机的指令。
编译器将源代码转换为虚拟机指令之后,虚拟机会执行这些指令。
执行过程中,虚拟机会访问一个预先写好的代码库,其中包括System和PrintStream类的实现,这是显示程序输出必不可少的类
(4). 组织工作
一定要了解你的文件位于文件夹层次体系中的什么位置。
提交文件进行评分和建立备份副本时,这个信息至关重要。
6. JVM、JRE 与 JDK 的关系
(1). JVM
JVM 全称 Java Virtual Machine 是java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。
也就是说.class文件并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
(2). JRE
JRE 全称 Java Runtime Environment 是java运行时环境,这里面包含了运行java程序所需要的所有类库,一台机器上只有安装了JRE才可以运行java程序
JRE 是包含 JVM的,并且还包含了一些运行java程序所需要的类库和资源文件等。
(3). JDK
JDK 全称 Java Development Kit 是java开发工具包,是Sun Microsystems针对Java开发员的产品。JDK 中包含了很多关于java程序开发的工具,例如编译工具javac,文档生成工具javadoc等等等等。
同理,JDK是包含JRE 和 JVM 的,并且在此基础上还包括了一些开发工具,调试工具,以及用于管理程序的管理工具等。
7. Java实现跨平台的原理
这里要从java的编译方式说起,java源代码编译之后并不是直接生成一个可执行文件(.exe),而是生成对应的java字节码文件(.class),这个字节码电脑的并不能运行,而是需要java虚拟机来再次进行解释,才能被cpu执行,也就是说,java程序并不是直接运行在cpu上的,而是运行在java虚拟机JVM上面的。
对于不同的从操作系统,有不同的java虚拟机。虽然是不同的虚拟机,但是他们可以识别相同的字节码文件。这样,就达到了一次编译,到处运行的目的,也就是java跨平台的原理。
二. Java语言基础
1. Java基本语法
编写Java程序时,应注意以下几点:
- 大小写敏感:Java是大小写敏感的,这就意味着标识符Hello与hello是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记Java是大小写敏感的),文件名的后缀为.java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的Java 程序由public static void main(String[] args)方法开始执行。
2. 代码注释和编码规范
在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了
注释不会被执行,是给我们写代码的人看的
书写注释是一个非常好的习惯
2.1 Java中注释有三种
- 单行注释
- 多行注释
- 文档注释
2.1.1 单行注释和多行注释
public class HelloWorld {
public static void main(String[] args) {
//单行注释
//输出一个Hello,World!
System.out.println("Hello,World!");
/*
这个是多行注释!
可以注释一段文字
*/
/**
* 文档注释
* @Description HelloWrold
* @Author pudding
*/
}
}
2.1.2 文档注释(JavaDoc)
javadoc命令是用来生成自己API文档的
参数信息:
@author 作者名
@version 版本号
@since 指明需要最早使用的jdk版本
@param 参数名
@return 返回值情况
@throws 异常抛出情况
package com.pudding.base;
/**
* @author pudding
* @version 1.0
* @since 1.8
*/
public class Doc {
String name;
/**
* @author pudding
* @param name
* @return
* @throws Exception
*/
public String test(String name) throws Exception{
return name;
}
}
通过命令行生成java注释文档
生成一系列的帮助文档
3. 标识符和关键字
3.1 标识符(自己取得别名)
Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符
注意事项:
- 最好是字母、数字、下划线、$组成
- 不能以数字开头
- 不能是Java的关键字
- 所有的名字要见名知意,便于自己和别人阅读
- 类名:首字母大写(大驼峰命名),如:StudentName
- 变量名:第二个单词开始首字母大写(小驼峰命名),如:studentName
3.2 关键字
不能用他们作为名字,关键字是java语言中有特殊含义的单词
关键字的特点:
- 关键字都是小写的
- 关键字在idea中有特殊颜色标记(如果你没有修改关键字的颜色,默认是蓝色的)
3.3 this关键字
3.3.1 this关键字概述
- this是一个关键字,全部小写
this是什么,在内存方面是怎么样的?
- 一个对象一个this
- this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身,所以严格意义上来说,this代表的就是“当前对象”
- this存储在堆内存当中对象的内部
this可以使用在实例方法中。
- 谁调用这个实例方法,this就是谁(就是这个对象)。所以this代表的是:当前对象
- this. 大部分情况下是可以省略的
package com.keyword;
public class ThisTest {
public static void main(String[] args) {
Customer c1 = new Customer("张三");
c1.shopping();
Customer c2 = new Customer("李四");
c2.shopping();
}
}
//顾客类
class Customer{
//属性
String name;
//构造方法
public Customer(){
}
public Customer(String s){
name = s;
}
//顾客购物方法
public void shopping(){
//这里的this是谁?this是当前对象
//c1调用shopping(),this是c1
//c2调用shopping(),this是c2
System.out.println(this.name + "正在购物!");
//this. 是可以省略的
//this. 省略的话,还是默认访问"当前对象"的name
System.out.println(name + "正在购物!");
}
}
静态方法不允许用this
- 因为this代表的是当前对象,而静态方法的调用不需要对象(静态方法中不存在当前对象),矛盾了
class Student{
String name = "zhangsan";
//静态方法
public static void m1(){
//System.out.println(name);
//this代表的是当前对象(实际上,以上代码本质是下面的)
//System.out.println(this.name);
//除非你这样写
Student s = new Student();
System.out.println(s.name);
}
}
this的执行原理
3.3.2 注意去理解this应用:
package com.keyword;
//分析:i变量在main方法中能不能访问??
public class ThisTest02 {
//实例变量,这个i必须先new对象才能访问
int i = 100;
//静态变量
static int k = 111;
//静态方法
public static void main(String[] args) {
//System.out.println(i);
//Error:无法从静态上下文中引用非静态 变量 i
//怎么样访问i
ThisTest02 tt = new ThisTest02();
System.out.println(tt.i);
//静态变量用"类名."方式访问
System.out.println(ThisTest02.k);
}
}
3.3.3 this什么时候不能省略
在实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下:this. 是不能省略的
this. 大大的增强了可读性
package com.keyword;
public class ThisTest03 {
public static void main(String[] args) {
Student s = new Student();
s.setNo(111);
s.setName("张三");
System.out.println("学号为:" + s.getNo());
System.out.println("姓名为:" + s.getName());
}
}
//学生类
class Student{
private int no;
private String name;
public Student(){
}
//构造方法也可以增强可读性
public Student(int no,String name){
this.no = no;
this.name = name;
}
//setter 和 getter方法
public int getNo() {
return no;
}
public void setNo(int no) {
//no = no 这两个no都是局部变量no,和实例变量no没有关系
//this.no 是指的实例变量
this.no = no; //this.的作用是:区分局部变量和实例变量
}
public String getName() {
//获取当前对象的名字
return name; //这个的源码是this.name,但是这里this.可以省略
}
public void setName(String name) {
this.name = name;
}
}
结果为:
学号为:111
姓名为:张三
3.3.4 在构造方法中使用this()
this除了可以使用在实例方法中,还可以用在构造方法中
通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:
this(实际参数列表);
通过一个构造方法1去调用构造方法2,可以做到代码复用。
但需要注意的是:构造方法1和构造方法2, 都是在同一个类当中。
- 作用:可以进行代码复用
- 对于this()的调用只能出现在构造方法的第一行
package com.keyword;
/*
1. 定义一个日期类,可以表示年月日信息
2. 需求中要求:
如果调用无参数构造方法,默认创建日期为:1970-1-1
当然,除了调用无参构造方法之外,也可以调用有参构造方法来创建日期对象
*/
public class ThisTest04 {
public static void main(String[] args) {
//调用无参数构造方法
Date d1 = new Date();
d1.detail();
//调用有参数构造方法
Date d2 = new Date(1999, 11, 11);
d2.detail();
}
}
class Date{
private int year;
private int month;
private int day;
public Date(){
//Error: 对this的调用必须是构造器中的第一个语句
//System.out.println(11);
// this.year = 1970;
// this.month = 1;
// this.day = 1;
this(1970,1,1); //调用另一个本类的构造方法
}
public Date(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
//打印日期的方法
public void detail(){
System.out.println(year + "年" + month + "月" + day + "日");
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
3.4 super关键字
3.4.1 super关键字概述
- super是一个关键字,全部小写
super和this的对比:
this:
-
this能出现在实例方法和构造方法中
-
this语法是:“this.” 、“this()”
-
this不能使用在静态方法中
-
this.大部分情况下是可以省略的,但是在区分局部变量和实例变量的时候不能省略
public void setName(String name){ this.name = name; }
-
this()只能出现在构造方法第一个行,通过当前的构造方法去调用"本类"中其他的构造方法,目的是:代码复用
super:
- super能出现在实例方法和构造方法中
- super语法是:“super.” 、“super()”
- super不能使用在静态方法中
- super.大部分情况下是可以省略的
- super()只能出现在构造方法第一个行,通过当前的构造方法去调用"父类"中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征
3.4.2 super()
表示通过子类的构造方法调用父类的构造方法
模拟现实世界中的这种场景:想要有儿子,需要先有父亲
重要结论:
当一个构造方法第一行:
- 既没有this(),也没有super()的话。默认会有一个super();
- 表示通过当前子类的构造方法调用父类的无参数构造方法,所以必须保证父类的无参数构造方法是存在的
注意:
- this()和super()不能共存,他们都是只能出现在构造方法第一行
- 无论是怎么样折腾,父类的构造方法是一定会执行的
- 在java语言中不管是new什么对象,最后老祖宗的Object类的无参数构造方法一定会执行(Object类无参数构造方法是处于栈顶部,最后调用,最先执行结束)
package com.keyword;
public class SuperTest01 {
public static void main(String[] args) {
new B();//输出:A类的无参数构造方法
//输出:B类的无参数构造方法
}
}
class A extends Object{
//建议手动将一个类的无参数构造方法写出来
public A(){
//super(); 这里也是默认有这一行代码的
System.out.println("A类的无参数构造方法");
}
//一个类如果没有手动提供任何构造方法,系统会默认提供一个无参数构造方法
//一个类如果手动提供一个构造方法,那么无参构造系统将不再提供
public A(int i){
//super();
System.out.println("A类的有参数构造方法");
}
}
class B extends A{
/*
public B(){
super();//默认这句话是省略的,但是系统也会默认调用
System.out.println("B类的无参数构造方法");
}
*/
public B(){
this("zhangsan"); //Error:对super的调用必须是构造器中的第一个语句
//调用父类中有参数构造方法
//super(123);
System.out.println("B类的无参数构造方法");
}
public B(String name){
super();
System.out.println("B类的有参数构造方法(String)");
}
}
结果:
A类的无参数构造方法
B类的有参数构造方法(String)
B类的无参数构造方法
3.4.3 在恰当的时候使用:super(实际参数列表)
super代表的是"当前对象(this)"的父类型特征
注意:在构造方法执行过程中一连串调用了父类的构造方法,父类构造方法又继续向下调用它的父类的构造方法,但实际上对象只创建了一个
思考:super(实参)到底是干啥的?
- super(实参)的作用是:初始化当前对象的父类型特征
- 并不是创建对象,实际上对象只创建了一个
super关键字代表什么呀?
- super关键字代表的就是"当前对象"的那部分父类型特征
package com.Super;
//测试程序
public class SuperTest03 {
public static void main(String[] args) {
CreditAccount ca1 = new CreditAccount();
System.out.println(ca1.getActno()+","+ca1.getBalance()+","+ca1.getCredit());
//结果:null,0.0,0.0
CreditAccount ca2 = new CreditAccount("1111",10000.0,0.999);
System.out.println(ca2.getActno()+","+ca2.getBalance()+","+ca2.getCredit());
//结果:1111,10000.0,0.999
}
}
//账户
class Account extends Object{
//属性
private String actno;//账号
private double balance;//余额
//构造方法
public Account() {
//super(); 默认存在
//this.actno = null; 默认存在
//this.balance = 0.0;默认存在
}
public Account(String actno, double balance) {
//super(); 默认存在
this.actno = actno;
this.balance = balance;
}
//setter and getter方法
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//信用账户
class CreditAccount extends Account{
//属性
private double credit;//信誉度,子类特有
//构造方法
public CreditAccount() {
//super(); 默认存在
//this.credit = 0.0; 默认存在
}
//有参构造
public CreditAccount(String actno, double balance,double credit) {
/*
私有的属性只能在本类中访问。
this.actno = actno;
this.balance = balance;
*/
//以上两行代码在恰当的位置,正好可以使用:super(actno,balance)
//通过子类的构造方法调用父类的构造方法
super(actno,balance);
this.credit = credit;
}
//setter and getter方法
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
super程序内存图理解
3.4.4 super内存图描述
package com.Super;
public class SuperTest04 {
public static void main(String[] args) {
Vip vip = new Vip("zhangsan");
vip.shopping();
}
}
class Customer{
String name;
Customer(){}
Customer(String name){
//super();
this.name = name;
}
}
class Vip extends Customer{
public Vip(){}
public Vip(String name){
super(name);
}
public void shopping(){
//this表示当前对象
System.out.println(this.name + "正在购物"); //zhangsan正在购物
//super表示的是当前对象的父类型特征 (super是this指向的那个对象中的一块空间)
System.out.println(super.name + "正在购物"); //zhangsan正在购物
//前面什么都没加,默认是this.name
System.out.println(name + "正在购物"); //zhangsan正在购物
}
}
因为是一个对象,所以都使用的是name同一个属性
3.4.5 super. 什么是不能省略
-
this. 和super. 大部分情况下都是可以省略的
-
this. 什么时候不能省略?
public void setName(String name){ this.name = name; }
-
super. 什么时候不能省略
父中有,子中又有,如果想在子类中访问"父的特征",super. 不能省略
package com.Super;
public class SuperTest05 {
public static void main(String[] args) {
Vip1 vip = new Vip1("zhangsan");
vip.shopping();
}
}
class Customer1{
String name;
Customer1(){}
Customer1(String name){
//super();
this.name = name;
}
}
class Vip1 extends Customer1{
//假设子类也有一个同名属性
//java中允许在子类中出现和父类一样的同名变量/同名属性
String name;
public Vip1(){}
public Vip1(String name){
super(name);
//this.name = null;默认存在
}
public void shopping(){
/*
java是怎么区分子类和父类的同名属性的?
this.name:当前对象的name属性
super.name:当前对象的父类型特征中的name属性
*/
System.out.println(this.name + "正在购物"); //null正在购物
System.out.println(super.name + "正在购物"); //zhangsan正在购物
System.out.println(name + "正在购物"); //null正在购物
}
}
3.4.6 super使用时后面必须有一个.
通过这个测试得出结论:
- super不是引用。super也不保存内存地址,super也不指向任何对象
- super只是代表当前对象内部的那一块父类型的特征
public class SuperTest06 {
//实例方法
public void doSome(){
//SuperTest06@154617c
System.out.println(this);
//输出"引用"时,会自动调用引用的toString()方法
//System.out.println(this.toString());
//编译错误:需要'.'
//System.out.println(super);
}
//this和super不能使用在static静态方法中
/*
public static void doOther(){
System.out.println(this);
System.out.println(super.xxx);
}
*/
//静态方法,主方法
public static void main(String[] args) {
SuperTest06 st = new SuperTest06();
st.doSome();
}
}
3.4.7 使用super调用父类方法
和属性的效果是类似的,在父和子中有同名的属性,或者说有相同的方法,如果此时想在子类中访问父中的数据,必须使用"super. "加以区分。
- super.属性名【访问父类的属性】
- super.方法名(实参)【访问父类的方法】
- super(实参)【调用父类的构造方法】
package com.Super;
public class SuperTest07 {
public static void main(String[] args) {
Cat c = new Cat();
c.yiDong();
/*
Cat move!
Cat move!
Animal move!
*/
}
}
class Animal{
public void move(){
System.out.println("Animal move!");
}
}
class Cat extends Animal{
//对move方法进行重写
public void move(){
System.out.println("Cat move!");
}
//单独编写一个子类特有的方法
public void yiDong(){
this.move();
move(); //默认this.move()
//super. 不仅可以访问属性,也可以访问方法
super.move();
}
}
三. Java数据类型
强类型语言
要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用
优点:严谨、安全例如:Java、C++
弱类型语言
变量可以使用前不先声明
优点:速度快例如:Python、JavaScript
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
因此Java的数据类型分为两大类
- 基本类型(primitive type)
- 引用类型(reference type)
3.1 数据类型在内存中的存储方式
基本数据类型的存储原理:所有的简单数据类型不存在“引用”的概念,基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,而Java语言里面八种数据类型是这种存储模型;
引用类型的存储原理:引用类型继承于Object类(也是引用类型)都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的;
区别:基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的
3.2 基本数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
(1). 整数类型
1. byte
- byte数据类型是8位、有符号的,以二进制补码表示的整数;
- 最小值是-128(-2^7);
- 最大值是127(2^7-1);
- 默认值是0;
- byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一;
- 例子:byte a = 100,byte b = -50。
2. short
- short数据类型是16位、有符号的以二进制补码表示的整数
- 最小值是-32768(-2^15);
- 最大值是32767(2^15 - 1);
- Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一;
- 默认值是0;
- 例子:short s = 1000,short r = -20000。
3. int
- int数据类型是32位、有符号的以二进制补码表示的整数;
- 最小值是-2,147,483,648(-2^31);
- 最大值是2,147,483,647(2^31 - 1);
- 一般地整型变量默认为int类型;
- 默认值是0;
- 例子:int a = 100000, int b = -200000。
4. long
- long数据类型是64位、有符号的以二进制补码表示的整数;
- 最小值是-9,223,372,036,854,775,808(-2^63);
- 最大值是9,223,372,036,854,775,807(2^63 -1);
- 这种类型主要使用在需要比较大整数的系统上;
- 默认值是0L;
- 例子: long a = 100000L,long b = -200000L。
(2). 浮点类型
1. float
- float数据类型是单精度、32位、符合IEEE 754标准的浮点数;
- float在储存大型浮点数组的时候可节省内存空间;
- 默认值是0.0f;
- 浮点数不能用来表示精确的值,如货币;
- 例子:float f1 = 234.5f。
2. double
- double数据类型是双精度、64位、符合IEEE 754标准的浮点数;
- 浮点数的默认类型为double类型;
- double类型同样不能表示精确的值,如货币;
- 默认值是0.0d;
- 例子:double d1 = 123.4。
(3). 字符类型
1. char
- char类型是一个单一的16位Unicode字符;
- 最小值是’\u0000’(即为0);
- 最大值是’\uffff’(即为65,535);
- char数据类型可以储存任何字符;
- 例子:char letter = ‘A’。
每一个字符存储的是ASCII码对应的二进制,每一个字符占八位,也就是一个byte长度的二进制数。而int类型是占4byte长度二进制。也就是16bit,本质这两个没有区别。
(4). 布尔类型
1. boolean
- boolean数据类型表示一位的信息;
- 只有两个取值:true和false;
- 这种类型只作为一种标志来记录true/false情况;
- 默认值是false;
- 例子:boolean one = true。
(6). 代码展示
1. 理解基本数据类型
public class Demo01 {
//八大基本数据类型
int num = 10; //最常用
byte num2 = 20;
short num3 = 30;
long num4 = 30L; //Long类型要在数字后面加个L
// 小数:浮点数
float num5 = 50.1F; //float类型要在数字后面加个F
double num6 =3.1415926;
//字符类型
char name = 'A';
//字符串,String不是关键字,是一个类
//String namea = "pudding";
//布尔值:是非
boolean flag = true;
//boolean flag = false;
}
2. 升华基本数据类型
java代码的面试题扩展
public class Demo02 {
public static void main(String[] args) {
//整数拓展: 进制 二进制0b 十进制 八进制0 十六进制0x
int i = 10;
int i2 = 010; //八进制0
int i3 = 0x10; //十六进制0x 0~9 A~F
System.out.println(i);
System.out.println(i2);
System.out.println(i3);
//=======================================================
//浮点数拓展? 银行业务怎么表示? 钱
//使用BigDecimal 数学工具类
//=======================================================
//float 有限 离散 舍入误差 大约 接近但不等于
//double
//最好完全避免使用浮点数进行比较
float f = 0.1f; //0.1
double d = 1.0 / 10; //0.1
System.out.println("======================");
System.out.println(f == d); //false
System.out.println(f);
System.out.println(d);
float d1 = 231231231231231231f;
float d2 = d1 + 1;
System.out.println(d1 == d2); //true
//=======================================================
//字符拓展?
//=======================================================
char c1 = 'a';
char c2 = '中';
System.out.println("======================");
System.out.println(c1);
System.out.println((int) c1); //强制转换
System.out.println(c2);
System.out.println((int) c2); //强制转换
//所有的字符本质还是数字
// Unicode 表 2个字节 0-65536 Excel:最长2的16次方
//U0000-UFFFF
char c3 = '\u0061'; //本质长得是这个样子的
System.out.println("======================");
System.out.println(c3); //a
//转义字符
// \t 制表符
// \n 换行
System.out.println("Hello\nWorld");
System.out.println("======================");
String sa = new String("hello world");
String sb = new String("hello world");
System.out.println(sa == sb);
String sc = "hello world";
String sd = "hello world";
System.out.println(sc == sd);
//对象 从内存分析
//布尔值扩展
boolean flag = true;
if (flag) {
} //新手
if (flag == true) {
}//老手
//Less is More! 代码要精简易读
}
}
3.2 引用数据类型
-
引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
-
对象、数组都是引用数据类型。
-
所有引用类型的默认值都是null。
-
一个引用变量可以用来引用与任何与之兼容的类型。
例子:Animal animal = new Animal(“giraffe”)。
对象是通过引用来操作的:栈—>堆(本质是指向对象的地址)
3.3. 数据类型转换
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换
运算中,不同的类型的数据先转化为同一类型,然后进行运算
(1). 自动类型转换
类型转换的举例理解
public class Demo03 {
public static void main(String[] args) {
int i = 128;
byte b = (byte)i; //内存溢出
double c = i;
//强制转换 (类型)变量名 高--低
//自动转换 低--高
System.out.println(i);
System.out.println(b);
System.out.println(c);
/*
注意点:
1. 不能对布尔值进行转换
2. 不能把对象类型转换为不相干的类型
3. 在把高容量转换到低容量的时候,强制转换
4. 转换的时候可能存在内存溢出,或者精度问题!
*/
System.out.println("=====================");
System.out.println((int)23.7); //23
System.out.println((int)-45.89f); //-45
System.out.println("=====================");
char d = 'a';
int e = d+1;
System.out.println(e);
System.out.println((char)e);
}
}
- byte、short、char运算的时候,最终都会转换为int类型,再做计算。
- 小类型和大类型一起进行运算时,最终结果会转换为大类型
(2). 强制类型转换
数据类型 变量名 = (目标数据类型)被转的数据
public class question27 {
public static void main(String[] args) {
double a = 100.123;
int b = (int) a;
System.out.println(b);
int c = 129;
byte d = (byte) c;
System.out.println(d);
int e = 126;
byte f = (byte) e;
System.out.println(f);
}
}
100
-127
126
类型转换的常见问题:
public class Demo04 {
public static void main(String[] args) {
//操作比较大的数的时候,注意溢出问题
//JDK7新特性,数字之间可以用下划线分割
int money = 10_0000_0000;
System.out.println(money); //1000000000
int years = 20;
int total = money*years; //-1474836480,计算的时候溢出了
System.out.println(total);
long total2 = money*years; //默认是int,转换之前已经存在问题了?
long total3 = money*((long)years); //提前转换就可以了
System.out.println(total3);
}
}
四. 变量和常量
4.1 字面量
大家不要被这个词搞晕了,它其实很简单,我们知道计算机是来处理数据的,字面量其实就是告诉程序员数据在程序中的书写格式。下面是常用的数据在程序中的书写格式
4.2 变量
变量是什么? 就是可以变化的量
Java是一种强类型语言。每个变量都必须声明其类型
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
声明变量的基本格式如下:
type varName [=value] [{,varName[=value]}];
//数据类型 变量名 = 值; 可以使用逗号隔开来声明多个同类型变量
注意事项
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束
4.2.1 声明变量
在 Java 语言中,所有的变量在使用前必须声明。声明变量的基本格式如下:
type 变量名[ = value][, 变量名[= value] ...] ;
int a, b, c; // 声明三个int型整数:a、b、c。
int d = 3, e, f = 5; // 声明三个整数并赋予初值。
byte z = 22; // 声明并初始化z。
double pi = 3.14159; // 声明了pi。
char x = 'x'; // 变量x的值是字符'x'。
定义变量就要告诉编译器这个变量的数据类型,这样编译器才知道需要配置多少空间给他,以及它能存放什么样的数据
程序运行过程中,空间的值是变化的,这个内存就称为变量
为了便于操作,给这个空间取个名字,称为变量名。内容空间的值就是变量值
public class Demo05 {
public static void main(String[] args) {
//int a,b,c; 不建议使用
//int a=1,b=2,c=3; 不建议使用,要保证程序的可读性
String name="pudding";
char x = 'x';
double pi = 3.14;
}
}
4.2.2 Java 语言支持的变量类型
- 局部变量:类的方法中的变量。
- 实例变量:独立于方法之外的变量,不过没有 static 修饰。
- 类变量:独立于方法之外的变量,用 static 修饰。
根据变量有效范围将变量分类:
1. 成员变量(方法体外,类体中声明)
在类体中所定义的变量被称为成员变量,成员变量在整个类中都有效。
这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
默认初始化
数字:0 0.0
char:u000
boolean:false
引用:null
举例:
class var
{
int x=45;
static int y=90;
}
类的成员变量可分为:
1.1. 类变量(静态变量)
y是静态变量。如果在成员变量的类型前面加上static关键字,这样的变量叫静态变量
静态变量的有效范围:
可以跨类,甚至可达到整个应用程序之内
静态变量的存取:
既可以在定义它的类内存取
还可以直接以 类名.静态变量 的方式在其他类内使用
- 类变量也称为静态变量,在类中以 static 关键字声明,但必须在方法、构造方法和语句块之外。
- 无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
- 静态变量除了被声明为常量外很少使用。常量是指声明为 public/private,final 和 static 类型的变量。常量初始化后不可改变。
- 静态变量储存在静态存储区。经常被声明为常量,很少单独使用 static 声明变量。
- 静态变量在程序开始时创建,在程序结束时销毁。
- 与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
- 默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是 false,引用类型默认值是 null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
- 静态变量可以通过:类名.变量名 的方式访问。
- 类变量被声明为 public static final 类型时,类变量名称必须使用大写字母。如果静态变量不是 public 和 final 类型,其命名方式与实例变量以及局部变量的命名方式一致。
1.2. 实例变量
x是实例变量
对象被称为实例
实例变量实际上就是:对象级别的变量
public class 明星类{
double height;
}
身高这个属性所有的明星对象都有,但是每一个对象都有自己的身高值。
假设创建10个明星对象,height变量应该有10份
所以这种变量被称为对象级别的变量。属于实例变量。
- 实例变量声明在一个类中,但在方法、构造方法和语句块之外;
- 当一个对象被实例化之后,每个实例变量的值就跟着确定;
- 实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
- 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
- 实例变量可以声明在使用前或者使用后;
- 访问修饰符可以修饰实例变量,一般通过对象名(引用).变量名方式访问;
- 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
- 实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定;
怎么访问实例变量?
- 在静态方法以及其他类中,使用完全限定名:引用.实例变量名
2. 局部变量(方法体内声明)
在类的方法、构造方法或者语句块中定义的变量被称为局部变量
在类的方法中声明的变量,包括方法的参数,都属于局部变量
变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁,局部变量占用的内存是自动释放。
- 访问修饰符不能用于局部变量
- 局部变量是在栈上分配的
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
局部变量作用范围:
在当前定义方法的代码块中有效,不能用于类的其他方法中
局部变量必须声明才可以使用。
局部变量生命周期:
当方法被调用时,Java虚拟机为方法中的局部变量分配内存空间
当该方法的调用结束后,则会释放方法中局部变量占用的内存空间
局部变量也将会被销毁
循环体内声明的变量其适用范围是从它声明到循环体结束。它包含如下所示的变量声明:
注:局部变量可与成员变量的名字相同,此时成员变量将会被隐藏
public class Val //新建类Val
{
static int times=3; //定义成员变量times
public static void main(String[] args) //主方法
{
int times=4; //定义局部变量times
System.out.println("times的值为:"+times);
}
}
结果:times的值为:4
4.2.3 变量的有效范围(作用域)
由于变量被定义出来之后只是暂时存在内存中,等到程序执行到某一点,该变量会被释放掉,也就是说变量有他的生命周期。
即变量的有效范围指的是程序代码能够访问该变量的区域,若超出该区域则会在编译时出现错误。
1. 变量的作用域代码理解
public class Demo06 {
//类变量 加static
static double salary = 2500;
//属性:变量
//实例变量:从属于对象,如果不自行初始化,这个类型的默认值为 0/0.0
//布尔值:默认是false,
//处理基本类型,其余默认值都是null;
String name;
int age;
//main方法
public static void main(String[] args) {
//局部变量:必须声明和初始化值
int i = 10;
System.out.println(i);
//变量类型 变量名字 = new Demo06();
Demo06 demo06 = new Demo06();
System.out.println(demo06.age);
System.out.println(demo06.name);
//类变量 加static
System.out.println(salary);
}
//其他方法
public void add(){
}
}
4.2.4 变量的命名规范
- 所有的变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰原则:monthSalary,除了第一个单词以外,后面的单词首字母大写
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则:Man,GoodMan
- 方法名:首字母小写和驼峰原则:run(),runRun()
4.2.5 变量的默认值
变量必须先声明,再赋值才能访问
注意:对于成员变量来说,没有手动赋值时,系统默认赋值
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
boolean | flase |
char | \u0000 |
引用数据类型 | null |
4.2.6 变量的注意事项
4.3 常量
常量(Constant):初始化(initialize)后不能再改变的值!不会变的值
所谓常量可以理解为一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。
4.3.1 声明常量
在Java中用final标志,声明方式和变量类似:
final 常量名=值;
例如:
final double PI=3.14
常量名一般使用大写字符
final修饰的变量一般添加static修饰。static final联合修饰的变量称为“常量”,常量名建议全部大写,每个单词之间采用下划线衔接。
- 常量实际上和静态变量一样,区别在于:常量的值不能变
- 常量和静态变量一样都是存储在方法区,并且都是类加载时初始化
- 常量一般都是公开的:public的,因为公开也无法修改
4.3.2 举例理解常量
public class Demo07 {
//static是修饰符,不存在先后顺序
static final double PI = 3.14;
public static void main(String[] args) {
System.out.println(PI);
}
}
4.3.3 Java语言支持一些特殊的转义字符序列
五. 运算符
计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:
- 算术运算符
- 关系运算符
- 位运算符
- 逻辑运算符
- 赋值运算符
- 其他运算符
(1). 赋值运算符
=
(2). 算术运算符
+ - * / % ++ --
package opertor;
public class Demo02 {
public static void main(String[] args) {
long a = 12312312313123131L;
int b = 123;
short c = 10;
byte d = 8;
System.out.println(a+b+c+d); //返回Long
System.out.println(b+c+d); //返回Int
System.out.println(c+d); //返回Int
//如果有一个数是Long,那么结果就是Long类型
//如果有一个数的double,那么结果就是Double类型
//其余的进行运算就是自动转换为int类型
}
}
package opertor;
public class Demo04 {
public static void main(String[] args) {
//++ -- 自增,自减 一元运算符
int a = 3;
int b = a++; //执行完这行代码后,先给b赋值,再自增
//a = a+1
System.out.println(a);
//a = a+1;
int c = ++a; //执行完这行代码前,先自增,再给b赋值
System.out.println(a);
System.out.println(b);
System.out.println(c);
//幂运算
double pow = Math.pow(2, 3);
System.out.println(pow);
}
}
- “+”符号与字符串运算的时候是用作连接符的,其结果依然是一个字符串。
- 能算则算,不能算就在一起。(计算机很聪明)
public class Main {
public static void main(String[] args) {
int a=5,b=4;
System.out.println(a+b);
System.out.println("abc"+5);
System.out.println("a"+2+"b");
}
}
9
abc5
a2b
- ++ 、-- 只能操作变量,不能操作字面量的。
tip:++在前先自增,++在后后自增
public class question30 {
public static void main(String[] args) {
int a = 10;
int b = a++;
System.out.println("b = " + b);
System.out.println("a = " + a);
a = 10;
int c = ++a;
System.out.println("c = " + c);
System.out.println("a = " + a);
}
}
b = 10
a = 11
c = 11
a = 11
public class question30 {
public static void main(String[] args) {
int count = 5;
/* 13 count = 7
* */
System.out.println(++count + ++count);
System.out.println(count);
/* 12 count = 7
* */
count = 5;
System.out.println(count++ + ++count);
System.out.println(count);
}
}
13
7
12
7
(3). 扩展赋值运算符
+= -= *= /=
package opertor;
public class Demo07 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a+=b; //a = a+b
a-=b; //a = a-b
System.out.println(a);
System.out.println(a+b);
//字符串连接符 + 当String出现在左侧,就会把右侧的操作数都转换为String类型,再进行连接
System.out.println(""+a+b);
//当字符串在后面,前面的依旧会进行运算
System.out.println(a+b+"");
System.out.println(a+b+"aa"+a+b);
}
}
结果:
10
30
1020
30
30aa1020
(4). 关系运算符
> < >= <= == != instanceof
package opertor;
public class Demo03 {
public static void main(String[] args) {
//关系运算符返回的结果:正确,错误 都是布尔值
int a = 10;
int b = 20;
int c = 21;
//取余,模运算
System.out.println(c%a);
System.out.println(a>b);
System.out.println(a<b);
System.out.println(a==b);
System.out.println(a!=b);
}
}
(5). 逻辑运算符
&& || !
package opertor;
//逻辑运算符
public class Demo05 {
public static void main(String[] args) {
//与(and) 或(or) 非(取反)
boolean a = true;
boolean b = false;
System.out.println("a && b:"+(a&&b)); //逻辑与运算:两个变量都为真,结果才为true
System.out.println("a || b:"+(a||b)); //逻辑或运算:两个变量有一个为真,结果才true
System.out.println("!(a && b):"+!(a&&b)); //如果是真,则变为假,如果是假,则变为真
//短路运算
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(d);
System.out.println(c);
}
}
短路与结果与逻辑与结果完全一样,只是执行过程不同
短路或结果与逻辑或结果完全一样,只是执行过程不同
(6). 位运算符
& | ^ ~ >> << >>>
package opertor;
public class Demo06 {
public static void main(String[] args) {
/*
A = 0011 1100
B = 0000 1101
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~B = 1111 0010
2*8 = 16 2*2*2*2
效率极高!!
<< 相当于*2
>> 相当于/2
0000 0000 0
0000 0001 1
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
*/
System.out.println(2<<3); //结果16
}
}
(7). 条件运算符(三目运算符)
?:
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
variable x = (expression) ? value if true : value if false
package opertor;
//三元运算符
public class Demo08 {
public static void main(String[] args) {
// x ? y:z
//如果x==true,则结果为y
//如果x==false,则结果为z
int score = 80;
String type = score < 60 ?"不及格":"及格"; //必须掌握
System.out.println(type);
}
}
(8). instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
(Object reference variable) instanceof (class/interface type)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,
那么结果为真。
String name = 'James';
boolean result = name instanceof String; // 由于name是Strine类型,所以返回真
如果被比较的对象兼容于右侧类型,该运算符仍然返回true。
class Vehicle {}
public class Car extends Vehicle {
public static void main(String args[]){
Vehicle a = new Car();
boolean result = a instanceof Car;
System.out.println( result);
}
}
结果为:true
(9). 运算符的优先级
当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及到运算符的优先级别的问题。在一个多运算符的表达式中,运算符优先级不同会导致最后得出的结果差别甚大。
六. Java类和对象
(1). Java主类定义
Java语言是面向对象的程序设计语言,Java程序的基本组成单元是类,类体中又包括属性与方法。
每一个应用程序都必须包含一个main()方法,含有main()方法的类称为主类。
(2). 编写主方法
main()方法是类体中的主方法。
该方法的public、static、void分别是main()方法的权限修饰符、静态修饰符、返回值修饰符,Java中的main()方法必须声明为public static void。
String[] args是一个字符串类型数组,他是main()方法的参数。
main()方法是程序开始执行的位置
(3). Java 对象和类
Java作为一种面向对象语言。支持以下基本概念:
-
多态
-
继承
-
封装
-
抽象
-
类:类是一个模板,它描述一类对象的行为和状态。
-
对象:对象是类的一个实例,有状态和行为。
例如,一条狗是一个对象, 它的状态有:颜色、名字、品种; 行为有:摇尾巴、叫、吃等。
-
实例
-
方法
-
消息解析
6.3.1 Java中的对象
现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。
拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。
对比现实对象和软件对象,它们之间十分相似。
软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。
在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成
6.3.2 Java中的类
通过下面一个简单的类来理解下Java中类的定义:
public class Dog{
String breed;
int age;
String color;
void barking(){
}
void hungry(){
}
void sleeping(){
}
}
一个类可以包含以下类型变量:局部变量、成员变量、类变量
一个类可以拥有多个方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog类的方法。
类是一个模板,是描述共同特征的一个模板,那么共同特征包括什么?
共同特征包括:状态特征 + 动作特征
进而得出:类 = 属性 + 方法
- 属性来源于:状态
- 方法来源于:动作
注意:在同一个类中调用方法是可以省略前面的前缀
- 在实例方法中可以省略:this. (对象名.)
- 在静态方法中可以省略:类名.
- 静态变量和实例变量也是同理
1. 构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}
2. 创建对象
对象是根据类创建的。在Java中,使用关键字new来创建一个新的对象。
创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字new来创建一个对象。
- 初始化:使用new创建对象时,会调用构造方法初始化对象。
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("Passed Name is :" + name );
}
public static void main(String []args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
结果:Passed Name is :tommy
3. 访问实例变量和方法
通过已创建的对象来访问成员变量和成员方法
/* 实例化对象 */
ObjectReference = new Constructor();
/* 访问其中的变量 */
ObjectReference.variableName;
/* 访问类中的方法 */
ObjectReference.MethodName();
举例理解:
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("Passed Name is :" + name );
}
public void setAge( int age ){
puppyAge = age;
}
public int getAge( ){
System.out.println("Puppy's age is :" + puppyAge );
return puppyAge;
}
public static void main(String []args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("Variable Value :" + myPuppy.puppyAge );
}
}
结果:
Passed Name is :tommy
Puppy's age is :2
Variable Value :2
6.3.3 源文件声明规则
当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。
-
一个源文件中只能有一个public类
-
一个源文件可以有多个非public类
-
一个java文件里面写多个类,和在不同java文件里面写不同的单个类,然后进行编译的时候生成的class文件是相同的,也就是说也是生成不同的class文件。
-
源文件的名称应该和public类的类名保持一致。
例如:源文件中public类的类名是Employee, 那么源文件应该命名为Employee.java。
-
运行只和class文件有关系,与java文件没关系
-
如果一个类定义在某个包中,那么package语句应该在源文件的首行。
-
如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
-
import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
七. 修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
- 访问控制修饰符 : default, public , protected, private
- 非访问控制修饰符 : final, abstract, static,synchronized 和 volatile
修饰符用来定义类、方法或者变量,通常放在语句的最前端。
public class className
{
// ...
}
private boolean myFlag;
static final double weeks = 9.5;
protected static final int BOXWIDTH = 42;
public static void main(String[] arguments)
{
// 方法体
}
7.1. 访问控制修饰符
Java 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持4种不同的访问权限
修饰符 | 含义 | 内容 |
---|---|---|
private | 私有的 | 只能在本类中访问 |
【default】 | 默认的 | 在同一包内可见,不使用任何修饰符 |
protected | 受保护的 | 对同一包内的类 && 所有子类可见。 |
public | 共有的 | 对所有类可见 |
(1). 默认访问修饰符-不使用任何关键字
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。
接口里的变量都隐式声明为public static final,
而接口里的方法默认情况下访问权限为 public。
(2). 私有访问修饰符-private
私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。
声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。
Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。
下面的类使用了私有访问修饰符:
public class Logger
{
private String format;
public String getFormat() {
return this.format;
}
public void setFormat(String format) {
this.format = format;
}
}
(3). 公有访问修饰符-public
被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。
如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。
(4). 受保护的访问修饰符-protected
被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。
Protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected
但是接口的成员变量和成员方法不能声明为 protected。
子类能访问 Protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。
(5). 访问控制和继承的规则
-
父类中声明为 public 的方法在子类中也必须为 public。
-
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public。不能声明为 private。
-
父类中声明为 private 的方法,不能够被继承。
(6). 访问控制权限修饰符可以修饰什么?
- 属性(4个都能用)
- 方法(4个都能用)
- 类(public 和默认能用,其他不行)
- 接口(public 和默认能用,其他不行)
9.2. 非访问控制修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
- static 修饰符,用来创建类方法和类变量。
- final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
- abstract 修饰符,用来创建抽象类和抽象方法。
- synchronized 和 volatile 修饰符,主要用于线程的编程。
(1). static 修饰符
1.1 static的概述
- static翻译为静态
- 所有static关键字修饰的都是类相关的,类级别的
- 所有的static修饰的都采用"类名. "的方式访问,不需要new对象
- static修饰的变量:静态变量(类变量)
- static修饰的方法:静态方法(类方法)
1.2 变量什么时候声明实例的,什么时候声明静态的?
如果这个类型的所有对象的某个属性值都是一样的,不建议定义为实例变量,浪费内存空间。
建议定义为类级别特征,定义为静态变量,在方法区中值保留一份,节省内存开销。
package com.Method;
public class StaticTest02 {
public static void main(String[] args) {
Chinese c1 = new Chinese("123456","pudding","中国");
Chinese c2 = new Chinese("456789","zhangsan","中国");
}
}
//定义一个类:中国人
class Chinese{
//身份证号码
//每个人的身份证号码不同,所以身份证应该是一个实例变量,一个对象一份
String idCard;
//姓名
//姓名也是一个人一个姓名,是实例变量
String name;
//国籍
//对于中国人这个类来说,国籍都是中国人,不会随着对象的改变而改变
//显然国籍并不是对象级别的特征,是整个类的特征
String country; //所以一般不这么写,会浪费空间
//无参构造
public Chinese(){
}
//有参构造
public Chinese(String s1,String s2, String s3){
idCard = s1;
name = s2;
country = s3;
}
}
对上述的代码进行改进
package com.Method;
public class StaticTest02 {
public static void main(String[] args) {
//访问中国人的国籍
//静态变量应该使用类名.的方式访问
System.out.println(Chinese.country); //中国
Chinese c1 = new Chinese("123456","pudding");
System.out.println(c1.idCard); //123456
System.out.println(c1.name); //pudding
System.out.println(c1.country); //中国(不建议这样写)
//c1是空引用
c1 = null;
//分析这里会不会出现空指针异常?
//不会出现空指针异常,因为静态变量不需要对象的存在。
//实际上以下代码在运行的时候是:System.out.println(Chinese.country);
System.out.println(c1.country);
Chinese c2 = new Chinese("456789","zhangsan");
}
}
//定义一个类:中国人
class Chinese{
//身份证号码
//每个人的身份证号码不同,所以身份证应该是一个实例变量,一个对象一份
String idCard;
//姓名
//姓名也是一个人一个姓名,是实例变量
String name;
//国籍
//对于中国人这个类来说,国籍都是中国人,不会随着对象的改变而改变
//显然国籍并不是对象级别的特征,是整个类的特征
//String country; 所以一般不这么写,会浪费空间
//加static的变量叫静态变量
//静态变量在类加载时初始化,不需要new对象,静态变量的空间就开出来了
//静态变量存储在方法区
static String country = "中国";
//无参构造
public Chinese(){
}
//有参构造
public Chinese(String s1,String s2){
idCard = s1;
name = s2;
}
}
1.3 方法什么时候定义实例方法,什么时候定义静态方法?
-
当这个方法体当中,直接访问了实例变量,这个方法一定是实例方法
-
我们以后开发中,大部分情况下,如果是工具类的话,工具类当中的方法一般都是静态的(静态方法有一个优点,是不需要new对象,直接采用类名调用,极其方便。工具类就是为了方便,所以工具类中的方法一般都是static的)
-
工具类就是为了方便编程而开发的一些类
什么时候方法定义为实例方法?
张三考试,得分90
李四开始,得分100
不同的对象参加考试的结果不同。
我们可以认定"考试"这个行为是与对象相关的行为
建议将"考试"这个方法定义为实例方法
package com.Method;
/*
类 = 属性 + 方法
属性描述的是:状态
方法描述的是:行为动作
一个方法代表一个动作。
此方法一般都是描述一个行为,如果说该行为必须由对象去触发。
那么该方法定义为实例方法
*/
public class StaticTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.getId());//0
}
}
class User{
//实例变量,需要对象
private int id;
private String name; //首先先分析的是,这个new是对象级别的,一个对象一份
//分析这个方法应该定义为实例方法还是静态方法?
//打印一个用户的名字这样一个方法
public void printName(){
//输出的是一个对象的name
System.out.println(name);
}
/*
不能使用如下方法
public static void printName(){
System.out.println(name);
}
*/
public void setId(int i){
id = i;
}
public int getId(){
return id;
}
/*
不能使用如下方法
public static int getId(){
}
*/
}
1.4 静态变量|方法的访问注意事项
-
静态变量(类变量):
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。静态变量也被称为类变量。局部变量不能被声明为static变量。
-
静态方法(类方法):
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
对类变量和方法的访问可以直接使用 类名.变量名 和 类名.方法名 的方式访问。
静态的变量或方法也可以通过对象名(引用).变量名|方法名访问,但是不建议这样使用,最好使用类名.变量名|方法名访问,不然会引起程序员产生困惑。
类{
//实例相关的都需要new 对象的,通过"引用. "访问
实例变量;
实例方法;
//静态相关的都是采用"类名. "访问,也可以使用"引用. ",只不过不建议
静态变量
静态方法
}
1.5 使用static关键字可以定义:静态代码块
什么是静态代码块,语法是什么?
static{
java语句;
java语句;
}
static静态代码块在什么时候执行呢?
- 类加载的时候执行,并且只执行一次
- 注意:静态代码块在类加载时执行,并且在main方法执行之前执行
- 静态代码块一般是按照自上而下的顺序执行。
静态代码块有啥作用?
-
第一:静态代码块不是那么常用(不是每个类当中都需要写的东西)
-
静态代码块这种语法机制实际上是sun公司给我们java程序员的一个特殊的时机,这个时机叫做:类加载时机
具体的业务 项目经理说:大家把我们所写的编程代码,只要是类加载了,请记录一下类加载的日志信息 (在哪年哪月哪日几时几分几秒,那个类加载到JVM中了) 这些内容就可以写在静态代码块中
1.6 静态代码块的执行顺序
package com.Method;
/*
栈:方法只要执行,会压栈(局部变量)
堆:new出来的对象都在堆中。垃圾回收器主要针对(实例变量)
方法区:类的信息,字节码信息,代码片段(静态变量)
方法的代码片段放在方法区,但是方法执行过程当中需要的内存在栈中
*/
public class StaticTest04 {
//静态变量在类加载的时候初始化
//静态变量存储在方法区
static int i = 100;
//静态代码块在类加载的时候执行
static {
System.out.println("i = "+i);
}
//实例变量
int k = 111;
static {
//static静态代码块在类加载的时候执行,并且只执行一次
//类加载时,k变量空间还没有开辟出来
//System.out.println(k);
//所以会报错:无法从静态上下文中引用非静态变量k
/*
System.out.println(name);
代码无法访问name变量
错误:非法前向引用
*/
//静态代码块和静态变量都在类加载的时候执行,时间相同,只能靠代码的顺序来决定谁先谁后
}
//静态变量在静态代码块下面
static String name = "pudding";
//入口
public static void main(String[] args) {
System.out.println("main begin");
}
}
总结:到目前为止,有顺序要求的java程序
- 第一:对于一个方法来说,方法体中的代码是有顺序的,遵循自上而下的顺序执行
- 第二:静态代码块1和静态代码块2是有先后顺序的
- 静态代码块和静态变量是有顺序的。
1.7 实例代码块的执行顺序
- 除了静态代码块之外,还有一种语句块叫:实例语句块
- 实例语句在类加载并没有执行
语法:
{
java语句
}
实例语句块什么时候执行?
- 只要是构造方法执行,必然在构造方法执行之前,自动执行"实例语句块"中的代码。
- 实际上这也是sun公司为java程序员准备的一个特殊的时机,叫做对象构建时机
1.8 static的举例理解
1. 静态变量和静态方法
package com.oop.demo07;
//static
public class Student {
private static int age; //静态变量
private double score;
public void run(){
go(); //非静态方法可以调用静态方法
}
public static void go(){
//静态方法可以调用静态方法,但是不能调用普通方法
}
public static void main(String[] args) {
Student s1 = new Student();
//静态属性
System.out.println(Student.age);
System.out.println(s1.age);
System.out.println(s1.score);
//静态方法
new Student().run();
Student.go();
go();
}
}
2. 静态导入包
package com.oop.demo07;
//静态导入包
import static java.lang.Math.PI;
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
System.out.println(Math.random());
//静态导入包之后,可以直接写
System.out.println(random());
System.out.println(PI);
}
}
3. 静态代码块
package com.oop.demo07;
public class Person {
//作用:赋初始值
{
//代码块(匿名代码块)
System.out.println("匿名代码块");
}
//第一个加载,只执行一次
static{
//静态代码块
System.out.println("静态代码块");
}
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println("================");
Person person1 = new Person();
}
}
(2). final 修饰符
用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
- final是java语言中的一个关键字
- final表示最终的,不可变的
- final可以修饰变量以及方法,还有类
- final控制不了能不能调用的问题,final修饰的表示最后的,不能变的,不能改变的
1.1 final修饰的类?
final修饰的类无法被继承
- final 类不能被继承,没有类能够继承 final 类的任何特性。
final class A{ //A没有子孙
}
//B类继承A类,相当于对A类的功能进行扩展。如果你不希望别人对A进行扩展
//你可以给A类加final关键字,这样的话A类就无法继承了
//错误:无法从最终A进行继承
/*
class B extends A{
}
*/
/*
错误:无法从最终String进行继承
class MyString extends String{
}
1.2 final修饰的方法?
final修饰的方法无法被覆盖,被重写
- 类中的 Final 方法可以被子类继承,但是不能被子类修改。
class C{
public final void doSome(){
System.out.println("C's doSome");
}
}
class D extends C{
/*错误
public void doSome(){
System.out.println("D's doSome");
}
*/
}
1.2 final修饰的变量?
final修饰的局部变量,一旦赋值不能重新赋值
- final修饰的变量只能赋一次值
public static void main(String[] args) {
//局部变量
final int i = 100;
//重写赋值
//i = 200; 错误:无法为最终变量i分配值
final int m;
//第一次赋值
m = 200;
//重新赋值
//m = 300; 错误:已分配变量m
}
final修饰的引用:
- 该引用只能指向1个对象,并且它只能永远指向该对象,无法再指向其他对象,并且在该方法执行过程中,该引用指向该对象之后,该对象不会被垃圾回收器回收。直到当前方法结束,才会释放空间。
- 被声明为final的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是对象本身的值可以改变,所有final里面的值可以改变。
- final 修饰符通常和 static 修饰符一起使用来创建类常量。这样的值是恒定不变的
/*
final修饰的变量永远只能赋一次值
final修饰的变量,如果这个变量是一个"引用"会怎么样??
*/
public class FinalTest02 {
public static void main(String[] args) {
Person p1 = new Person(20);
System.out.println(p1.age); //20
//代码不管怎么变化,p也是一个变量(只不过这里有一个特殊的名字:引用)
final Person p = new Person(30); //final Person p = 0x1111
//错误:无法为最终变量p分配值
//p = new Person(30); //p = 0x2356
p.age = 40;
System.out.println(p.age); //40
}
}
class Person{
int age;
public Person(){
}
public Person(int age){
this.age = age;
}
}
final修饰的实例变量:
- 实例变量如果没有手动赋值的话,系统会赋默认值
- final修饰的实例变量,系统不负责赋默认值,要求程序员必须手动赋值,但是只要赶在系统赋默认值之前赋值就行。这个手动赋值,在变量后面赋值可以,在构造方法中赋值也可以
- final修饰的变量一般添加static修饰。static final联合修饰的变量称为“常量”,常量名建议全部大写,每个单词之间采用下划线衔接。
/*
final修饰的实例变量?
重点:final修饰的变量只能赋值一次
实例变量如果没有手动赋值的话,系统会赋默认值。
实例变量什么时候赋值(初始化)
构造方法执行的过程中赋值(new的时候赋值)
*/
public class FinalTest03 {
public static void main(String[] args) {
}
}
class User{
//实例变量
//final int age; //Error:变量 age 未在默认构造器中初始化
//实例变量
//可以,因为程序员手动赋值了
final double height = 1.8;
//实例变量
final double weight;
//构造方法
public User(){
this.weight = 80;//只要我赶在系统赋默认值之前赋值就行
//this.weight = 81; 这个不可以
}
}
/*
上一个例子的结论:
final修饰的实例变量,必须手动赋值,并且只能赋一次值
*/
public class FinalTest04 {
public static void main(String[] args) {
System.out.println(Chinese.COUNTRY); //中国
// Chinese.COUNTRY = "美国"; 错误:常量的值是不能改变的
}
}
class Chinese{
String idCard;
String name;
//实例变量在堆中,一个对象一份,100个对象100份
//实例变量既然使用final修饰了,说明这里的值不会改变,还有必要声明为实例变量吗?
//该实例变量前面应该添加static关键字,变成静态的,存储在方法区
//final String country = "中国";
public static final String COUNTRY = "中国";
/*
总结:
i永远都是10,创建100个对象,i也是10
i是10是永远不会发生改变的,既然这样,没有必要声明为实例变量,
最好是静态的,节省内存空间
*/
static final int i = 10;
}
(3). abstract 修饰符
用来创建抽象类和抽象方法。
-
抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
抽象类可以包含抽象方法和非抽象方法。
abstract class Caravan { private double price; private String model; private String year; public abstract void goFast(); //抽象方法 public abstract void changeColor(); }
-
抽象方法
抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成 final 和 static。
任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,
例如:public abstract sample();
public abstract class SuperClass { abstract void m(); //抽象方法 } class SubClass extends SuperClass { //实现抽象方法 void m(){ ......... } }
(4). synchronized 和 volatile 修饰符
主要用于线程的编程。
-
synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。Synchronized 修饰符可以应用于四个访问修饰符。
-
transient 修饰符
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机 (JVM) 跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
-
volatile修饰符
volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。
八. Java方法详解
对于一个Java程序来说,如果没有方法,会存在什么问题?
代码无法得到复用,使用该功能的时候,直接调用方法即可,这样代码得到复用
3.1 何谓方法?
System.out.println(),那么它是什么呢?
理解:调用系统类里面的标准输出对象out中的方法println()方法
println()是一个方法(Method),而System是系统类(Class),out是标准输出对象(Object)。
这句话的用法是调用系统类System中的标准输出对象out中的方法println()。
Java方法是语句的集合,它们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
本质来说: 方法是可以完成某个特定功能的并且可以被重复利用的代码片段,在C语言中,方法被称为函数
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样有利于我们后期的扩展
package com.pudding.method;
public class Demo01 {
//main方法
public static void main(String[] args) {
//实际参数:实际调用传递给他的参数
int sum = add(1, 2);
System.out.println(sum);
}
//加法
//形式参数,用来定义作用
public static int add(int a,int b){
return a+b;
}
}
3.2 方法的定义
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
方法是在类体里面定义的,main方法是程序的入口,除了main方法,其他的方法都需要调用,main方法由JVM调用。方法是自上而下依次逐行执行
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
[修饰符] 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
修饰符:
这个是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型
返回值类型:
方法可能会有返回值。returnValueType是返回值的数据类型。
返回值类型可以是Java中合法的类型即可(基本、引用数据类型)
有些方法回显所需的操作,但没有返回任何值。
在这种情况下,returnValueType也不能是空白,必须写上关键字void
表示该方法执行结束后不返回任何结果
方法名:
是方法的实际名字。方法名和参数表共同构成方法签名
有一定的命名规范,首字母小写+后面的每个单词首字母大写(驼峰命名)
参数类型:
参数像是一个占位符。当方法被调用时,传递值给参数。
这个值被称为实参或变量。
参数列表是指方法的参数类型、顺序、参数的个数。
参数是可选的,方法可以不包含任何参数
形式参数:在方法被调用时用于接收外界输入的数据,其中每个参数都是局部变量,方法结束后内存释放
形式参数个数是:0~N个,形参有多个的话,需要用,隔开
实参:调用方法是实际传给方法的数据
package com.oop.demo01;
public class Demo03 {
public static void main(String[] args) {
//实际参数和形式参数的类型要一一对应
int add = new Demo03().add(1,2);
System.out.println(add);
}
public int add(int a,int b){ //形式参数
return a+b;
}
}
方法体:
由Java语句构成,Java语句以;结尾
方法体当中编写的是业务逻辑代码,完成某个特定的功能
在方法体中的代码遵循自上而下的顺序依次逐行执行
在方法体中处理业务逻辑代码的时候需要数据,数据来源就是这些形参
tip:
- 调用有返回值的方法,有3种方式:
- 1、可以定义变量接收结果
- 2、或者直接输出调用,
- 3、甚至直接调用;
- 调用无返回值的方法,只有1种方式:
- 1、只能直接调用。
3.2.1 break和return的区别
不是一个级别。
break:跳出switch,结束循环
return 用来终止离他最近的一个方法,return之后不可以在写其他语句,因为是无效的,会报错的
- return:跳出并立即结束所在方法的执行
- break:跳出并结束当前所在循环的执行
- continue:结束当前所在循环的当次继续,进入下一次执行
package com.Method;
public class MethodTest01 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i==5){
//break; 终止for循环
//return; 终止当前的方法,和break不是一个级别的
}
System.out.println("i = " + i);
}
System.out.println("hello world!");
}
}
package com.oop.demo01;
import java.io.IOException;
//Demo01 类
public class Demo01 {
//main 方法
public static void main(String[] args) {
}
/*
修饰符 返回值类型 方法名(...){
//方法体
return 返回值;
}
*/
//return 结束方法,返回一个结果!
public String sayHello(){
return "hello world";
}
public void hello(){
return;
}
public int max(int a,int b){
return a>b ? a:b; //三元运算符!
}
//数组下标越界异常:Arrayindexoutofbounds
public void readFile(String file) throws IOException{
}
}
3.2.2 缺少返回语句
package com.Method;
public class MethodTest01 {
public static void main(String[] args) {
}
public static int m(){
boolean flag = true;//编译器不负责运行程序,编译器只讲道理
//对于编译器来说,编译器只知道flage变量是boolean类型
//编译器会认为flag可能是false,有可能是true
if (flag){
//编译器觉得,以下这些代码可能会执行,当然也可能不会执行
//编译器为了确保程序不出现任何异常,所以编译器说:缺少返回语句
return 1;
}
}
}
解决方法
public static int m(){
boolean flag = true;
if (flag){
return 1;
}else{
return 1;
}
/*
或者
if (flag){
return 1;
}
return 1;
两者本质上是一样的
*/
或者:
public static int m(){
boolean flage = true;
return flage ? 1 : 0;
}
}
3.3 方法的分类
3.3.1 静态方法
定义:
类/接口中,用static修饰的方法。
格式:
权限修饰符 static 返回值类型 方法名(形参列表){
}
(static可与final,private共存)
特点:
- 静态方法属于整个类,不单独属于类的某一个对象。
- 静态方法类加载便存在,比对象先存在,随类而消亡。
调用:
- 类名/接口名.静态方法 或者 实例对象.静态方法名 两种方式调用,建议使用第一种。
- 同一个类里面,方法的调用可以省略类名,直接写:静态方法名()进行调用
- 静态方法可以调用静态成员,不能调用非静态成员。(其先于非静态成员产生)。
继承性:
- 类(包括抽象类)其静态方法是可以被继承的。即可以通过:子类名/子类对象.静态方法的方式进行调用。
- 接口的静态方法不能被其实现类或者子接口继承。即不能采用:实现类名/实现类对象名/子接口名.静态方法名的方式进行调用。
3.3.2 非静态方法(实例方法)
定义:
类中/接口中无static修饰的方法,类中无特殊修饰符,接口中用default修饰。
实例相关的有:实例变量、实例方法
- 实例变量是对象变量。
- 实例方法是对象方法
- 实例相关的都需要先new对象,通过"引用 ." 的方式去访问
格式:
类或者抽象类中:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
接口中:
权限修饰符 default 返回值类型 方法名(形参列表){
方法体
}
特点:
实例/默认方法属于类的实例对象。随对象创建而加载,对象消失而消亡。
调用:
只能通过:实例对象/接口的实现实例对象名(引用).方法名进行调用。
继承性:
可以被子类继承(接口亦然)。
// 接口
public interface MyInter3 {
public default void eat(){
System.out.println("走起,咱今天吃火锅去!");
}
}
// 类
public class Myclass3 {
public void eat(){
System.out.println("走起,咱今天吃海鲜去!");
}
}
// 测试类
public class Test03 {
public static void main(String[] args) {
// Myclass3.eat(); // 错误,不能使用类名调用。
new Myclass3().eat(); // 可以通过实例对象调用
// MyInter3.eat(); // 错误,不能使用接口名调用
new MyInter3(){}.eat(); // 可以通过接口的实现类对象调用,这里匿名内部类实际上是接口的实现类对象。
}
}
3.3.3 构造方法
1. 什么是构造方法?有什么用?
构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值(注意:实例变量没有手动赋值的时候,系统会赋默认值)
重点: 当一个类没有提供任何构造方法,系统会默认提供一个无参构造方法。(而这个构造方法被称为缺省构造器)
2. 怎么调用构造方法?
使用new运算符来调用构造方法
语法格式:
new 构造方法名(实际参数列表);
3. 构造方法的语法结构?
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
通常在构造方法体中给属性赋值,完成属性的初始化
}
- 构造方法名和类名必须一致
- 构造方法不需要指定返回值类型,也不能写void,写上void就表示普通方法
4. 构造方法存在条件
-
当一个类中没有提供任何构造方法,系统会默认提供一个无参数的构造方法。这个无参的构造方法叫做缺省构造器。
-
当一个类中手动的提供了构造方法,那么系统将不再默认提供无参数的构造方法
-
无参数构造方法,和有参数的构造方法都可以调用
Student x = new Student(); Student y = new Student(123);
-
构造方法支持方法重载,在一个类当中构造方法可以有多个,并且所有的构造方法名字都是一样的。(参数列表不同)
-
对于实例变量来说,只有你在构造方法中没有手动给它赋值,统一都会默认赋值。默认赋系统值。
5. 总结
构造方法:
类或者抽象类中(接口没有),与类名同名,无返回值,不能用static修饰。
格式:
权限修饰符 类名(参数) {
方法体
}
作用:
初始化实例对象。
通常会使用构造方法给一个类的实例变量赋初值,或者执行其它必要的步骤来创建一个完整的对象。
不管你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个默认构造方法,它把所有成员初始化。
调用:
通过调用构造方法,创建类的实例对象时。
new 构造方法名();
继承性:
无参构造其子类会自动调用父类的无参构造super()。含参构造不会被继承。
注意:
构造方法分为含参构造和无参构造。JAVA会在编译时自动生成无参构造,但若书写了含参构造,则必须自己写无参构造,否则只能调用含参构造。
3.3.4 抽象方法
定义:
抽象类或者接口中,用abstract修饰的方法
格式:
权限修饰符 abstract 返回值类型 方法名(参数);(abstract 不能与static 和final修饰符共存)
特点:
必须被子类重写,无方法体
调用:
子类重写后。按照实例方式的调用方式进行调用。
继承性:
必须重写,谈不上继承不继承。
// 抽象类
public abstract class AbstractClass01 {
// 抽象方法
public abstract void fun();
}
// 子类继承于抽象类
public class Myclass4 extends AbstractClass01{
// 重写抽象方法
@Override
public void fun() {
System.out.println("玩得真开心");
}
}
// 测试类
public class Test04 {
public static void main(String[] args) {
// 创建对象使用重写后的抽象方法
new Myclass4().fun();
}
}
// 运行结果
玩得真开心
3.3.5 私有方法
定义:
使用private权限修饰符修饰的方法
格式:
private 返回值类型 方法名(参数){方法体} (private 可以static,final,abstract共存)
特点:
private只属于本类,不属于其子类对象。
调用:
本类可直接调用,其他类或者子类或者该类对象,只能通过类中其他方法进行间接调用。
继承性:
可以被子类继承(接口亦然),当也只能通过类中其他方法进行间接调用。
// 父类包含私有方法
public class Myclass5 {
// 私有方法
private void eat() {
System.out.print("吃 ");
}
// 公有实例方法
public void fun(){
eat();
System.out.println("玩");
}
}
// 子类
public class Myclass6 extends Myclass5{
}
// 测试类
public class Test05 {
public static void main(String[] args) {
// 创建对象
Myclass5 my = new Myclass5();
// my.eat; // 报错,不能直接调用私有方法。
my.fun(); // 可以间接调用私有方法
// 子类对象间接调用
System.out.println("分割线===========");
new Myclass6().fun();
}
}
3.4 方法的调用
方法必须调用才能执行
当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
3.4.1 静态方法的调用
调用方法:类名.方法名(实参列表)
注意:
a()方法调用b()方法的时候,a和b方法都在同一个类中,类名可以省略
但是当在一个java文件里面写了多个类的时候,
并且多个类中有共同的方法,当主方法调用这些不同类中的相同名字的方法时,
可能出现错误,主方法不知道是哪个类调用的,所以此时不可以省略类名
总结:建议所有的方法调用不省略类名
Java支持两种调用方法的方式,根据方法是否返回值来选择
-
当方法返回一个值的时候,方法调用通常被当做一个值,通常用一个变量去接受他
int large = max(30,40);
-
如果方法返回值是void,方法调用一定是一条语句
System.out.println("Hello,pudding");
package com.pudding.method;
public class Demo02 {
public static void main(String[] args) {
int max = max(10,20);
System.out.println(max);
}
//比大小
public static int max(int num1, int num2){
int result = 0;
if (num1 == num2) {
System.out.println("num1==num2");
return 0; //终止方法
}
if (num1>num2){
result = num1;
}else {
result = num2;
}
return result;
}
}
3.4.2 非静态方法的调用
必须先将要调用的类实例化然后通过
调用方法:对象名.方法名(实参列表)
3.4.3 方法执行的时候内存的变化
package com.Method;
//局部变量:只在方法体中有效,方法结束后,局部变量的内存就释放了
//JVM三块主要的内存,栈内存、堆内存、方法区内存
//方法区最先有数据:方法区中放代码片段。存放clas字节码
//栈内存:方法调用的时候,该方法需要的内存空间在栈中分配
//方法只有在调用的时候才会在栈中分配内存空间,并且调用时就是压栈
//方法执行结束后,该方法所需要的空间就会释放,此时发生弹栈动作
public class MethodTest01 {
//主方法,入口
public static void main(String[] args) {
System.out.println("main begin");
int x = 100;
m2(x);
System.out.println("main over");
}
public static void m1(int i){
System.out.println("m1 begin");
m2(i);
System.out.println("m1 over");
}
public static void m2(int i){
System.out.println("m2 begin");
m3(i);
System.out.println("m2 over");
}
public static void m3(int i){
System.out.println("m3 begin");
System.out.println(i);
System.out.println("m3 over");
}
}
方法被调用的时候,是进入到栈内存中运行,先进后出
3.5 方法重载
3.5.1 什么是代码重载(Overload)?
重载就是在一个类中,有相同的函数名称,但形参不同的函数
方法的重载规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)
- 方法的重载和返回类型无关
- 方法的重载和修饰符列表无关
tip:
- 一个类中,只要一些方法的名称相同、形参列表不同,那么它们就是方法重载了,其它的都不管(如:修饰符,返回值类型是否一样都无所谓)。
- 形参列表不同指的是:形参的个数、类型、顺序不同,不关心形参的名称。
实现理论:
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,
以选择对应的方法,如果匹配失败,则编译器报错。
package com.pudding.method;
public class Demo02 {
public static void main(String[] args) {
double max = max(10,20);
System.out.println(max);
}
//比大小
public static int max(int num1, int num2){
int result = 0;
if (num1 == num2) {
System.out.println("num1==num2");
return 0; //终止方法
}
if (num1>num2){
result = num1;
}else {
result = num2;
}
return result;
}
public static double max(double num1, double num2){
double result = 0;
if (num1 == num2) {
System.out.println("num1==num2");
return 0; //终止方法
}
if (num1>num2){
result = num1;
}else {
result = num2;
}
return result;
}
}
println就是sun公司给我们写好的方法重载,可以打印任何类型的内容
3.5.2 为什么需要方法重载?
方法重载机制的缺点:
- sumInt、sumLong、sumDouble不是功能相同,而是功能相似
- 功能相似,重复代码,导致代码不美观
- 程序员需要记忆更多的方法名称
package com.Method;
/*
方法重载机制的缺点:
1. sumInt、sumLong、sumDouble不是功能相同,而是功能相似
2. 功能相似,重复代码,导致代码不美观
3. 程序员需要记忆更多的方法名称
*/
public class OverloadTest01 {
public static void main(String[] args) {
int x = sumInt(10,20);
System.out.println(x);
long y = sumLong(10L,20L);
System.out.println(y);
double z = sumDouble(10.0,20.0);
System.out.println(z);
}
//定义一个计算int类型数据的求和方法
public static int sumInt(int a, int b){
return a + b;
}
//定义一个计算long类型数据的求和方法
public static long sumLong(long a, long b){
return a + b;
}
//定义一个计算double类型数据的求和方法
public static double sumDouble(double a, double b){
return a + b;
}
}
使用方法重载机制,解决之前的两个缺点
优点1:代码整齐美观
优点2:功能相似,可以让方法名相同,更易于以后的代码编写
在java语言中,是怎么进行方法区分的呢?
首先java编译器会通过方法名进行区分
但是在java语言中允许方法名相同的情况出现
如果方法名相同,编译器会通过方法的参数类型进行方法的区分
package com.Method;
/*
使用方法重载机制,解决之前的两个缺点
优点1:代码整齐美观
优点2:功能相似,可以让方法名相同,更易于以后的代码编写
在java语言中,是怎么进行方法区分的呢?
首先java编译器会通过方法名进行区分
但是在java语言中允许方法名相同的情况出现
如果方法名相同,编译器会通过方法的参数类型进行方法的区分
*/
public class OverloadTest02 {
public static void main(String[] args) {
//对于程序员来说,只需记忆一个即可
System.out.println(sum(10,20));
System.out.println(sum(10L,20L));
System.out.println(sum(10.0,20.0));
}
//定义一个计算int类型数据的求和方法
public static int sum(int a, int b){
System.out.println("int求和");
return a + b;
}
//定义一个计算long类型数据的求和方法
public static long sum(long a, long b){
System.out.println("long求和");
return a + b;
}
//定义一个计算double类型数据的求和方法
public static double sum(double a, double b){
System.out.println("double求和");
return a + b;
}
}
什么时候需要考虑使用方法重载?
在同一个类当中,如果功能1和功能2他们的功能是相似的
那么可以考虑将他们的方法名一致,这样代码既美观,又便于后期的代码编写
注意:方法重载不能随便使用,如果两个功能压根不相干,不相似,根本没关系,此时两个方法使用重载机制的话,会导致编码更麻烦,无法进行方法功能区分
什么时候代码会发生重载?
条件1:在同一个类当中
条件2:方法名相同
条件3:参数列表不同(参数的个数、类型、顺序不同)
只要同时满足以上3个条件,那么我们可以认定方法和方法之间发生了重载机制
3.6 命令行传参
有时候你希望运行一个程序时候在传递给他消息,这要靠传递命令行参数给main()函数实现
命令行参数是在执行程序时候紧跟在程序名字后面的信息。
package com.pudding.method;
public class Demo03 {
public static void main(String[] args) {
//args.length数组长度
for (int i = 0; i < args.length; i++) {
System.out.println("args["+i+"]:"+args[i]);
}
}
}
3.7 可变参数
JDK1.5开始,Java支持传递同类型的可变参数给一个方法
在方法声明中,在指定参数类型后加一个省略号(…)
typeName... parameterName
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数都必须在它之前声明
package com.pudding.method;
public class Demo04 {
public static void main(String[] args) {
//调用可变参数的方法
printMax(34,3,3,2,56,6);
printMax(new double[]{1,2,3});
}
public static void printMax(double... numbers){
if (numbers.length == 0){
System.out.println("No argument passed");
return;
}
double result = numbers[0];
//排序
for (int i=1; i<numbers.length; i++){
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("The max value is "+result);
}
}
3.8 递归
A方法调用B方法,我们很容易理解!
递归就是:A方法调用A方法!就是方法自己调用自己
利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需的多次重复计算,大大地减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合
递归没有结束条件的时候,会发生栈的内存溢出错误(Stack Over flow Error),所以递归必须要有结束条件
- 递归有结束条件也可能发送内存栈溢出错误,因为有可能递归太深了,栈内存不够,因为一直在压栈
- 在实际开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量使用循环来做。应为循环的效率高,耗费内存少。递归耗费内存大,递归使用不当会导致JVM死掉(但在极少数情况下,不用递归,这个程序没法实现)
package com.Method;
public class RecursionTest01 {
//入口
public static void main(String[] args) {
doSome();
}
public static void doSome(){
System.out.println("doSome begin");
//doSome既然是一个方法,那么doSome当然可以调用自己
//目前这个递归是没有结束条件的,
//但是栈的内存是有限的,最终会导致栈内存溢出
doSome();
System.out.println("doSome over");
}
/*
一直不断的重复调用自己,也就是JVM进行不断的压栈操作,没有结束条件的话,就是无法进行出栈操作
public static void doSome(){
System.out.println("doSome begin");
//doSome既然是一个方法,那么doSome当然可以调用自己
doSome();
System.out.println("doSome over");
}
public static void doSome(){
System.out.println("doSome begin");
//doSome既然是一个方法,那么doSome当然可以调用自己
doSome();
System.out.println("doSome over");
}
public static void doSome(){
System.out.println("doSome begin");
//doSome既然是一个方法,那么doSome当然可以调用自己
doSome();
System.out.println("doSome over");
}
....
*/
}
递归结果包括两个部分:
- 递归头:什么时候不调用自身的方法。如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法。
package com.pudding.method;
public class Demo05 {
public static void main(String[] args) {
System.out.println(f(5));
}
//1! 1
//阶乘2! 2*1
//5! 5*4*3*2*1
public static int f(int n){
if (n==1){
return 1;
}else {
return n*(f(n-1));
}
}
}
3.9 Java的参数传递机制
在java语言中,方法调用时参数传递,和类型无关,都是将变量中保存的那个值传过去,这个值可能是一个数字123,也可能是一个java对象的内存地址0x1234
Java的参数传递机制类型分为:
- 基本类型的参数传递
- 引用类型的参数传递
但是Java的参数传递机制都是:值传递(指的是在传输实参给方法的形参的时候,传输的是实参变量中存储的值的副本)
- 基本类型的参数传输存储的数据值
- 引用类型的参数传输存储的地址值
3.9.1 基本类型的参数传递
package com.Method;
/*
java中规定:参数传递的时候,和类型无关,不管是基本数据类型,还是引用数据类型
统一都是将盒子中保存的那个值复制一份,传递下去
java中只有一个规定:参数传递的时候,一定是将盒子中的东西复制一份传递过去
内存地址也是值,也是盒子中保存的一个东西
*/
public class Test01 {
public static void main(String[] args) {
int x = 100;
int y = x;// x赋给y,是将x变量中保存的100复制一份传给了y
//局部变量,域是main
int i = 10;
// 将i变量中保存的10复制一份,传给add方法
add(i);
System.out.println("main---->"+i); //10
}
public static void add(int i){
i++;
System.out.println("add---->"+i); //11
}
}
package com.oop.demo01;
//值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println(a); //结果为1
Demo04.change(a);
System.out.println(a); //结果为1
}
//返回值为空
public static void change(int a){
a = 10;
}
}
3.9.2 引用类型的参数传递
java中关于方法调用时参数传递实际上只有一个规则:
-
不管你是基本数据类型还是引用数据类型,实际上在传递的时候都是将变量中保存的那个值复制一份,传过去。
int x = 1; int y = x; 把x中保存的1复制一份传给y x和y都是两个局部变量 Person p1 = 0x1234; Person p2 = p1; 把p1中保存的0x1234复制一份传给p2 p1和p2都是两个局部变量
package com.Method;
public class Test02 {
public static void main(String[] args) {
Person p = new Person();
p.age = 10;
add(p);
System.out.println("main---->"+p.age);
}
//方法的参数可以是基本数据类型,也可以是引用数据类型
public static void add(Person p){
p.age++;
System.out.println("add---->"+p.age);
}
}
class Person{
//年龄属性,成员变量中的实例变量
int age;
}
package com.oop.demo01;
//引用传递:对象,但是本质还是值传递
//对象、内存
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); //null
Demo05.change(person);
System.out.println(person.name);//pudding
}
public static void change(Person person){
//person是一个对象:指向的--->Person person = new Person();这个具体的人,可以改变属性!
person.name = "pudding";
}
}
//定义了一个person类,有一个属性:name
class Person{
String name; //默认值值为null
}
3.10 方法大总结
package com.keyword;
/*
程序再怎么变换,万变不离其宗,有一个固定的规律
所有的实例相关的都是先创建对象,通过"引用."来访问
所有的静态相关的都是直接采用"类名."来访问
大结论:
只要负责调用的方法a和被调用的方法b在同一个类中:
this. 可以省略
类名. 可以省略
*/
public class Review {
int i = 100;
static int j = 1000;
public void m1(){
//访问其他类静态方法
T.t1(); //这里的T是不能省略的,因为不在同一个类中
}
public void m2(){
//访问其他类的实例方法
T t = new T(); //必须先new对象然后才能访问实例方法t2()
t.t2();
}
//实例方法
public void x(){ //这个方法是实例方法,执行这个方法的过程中,当前对象是存在的
m1(); //系统自动加this.m1()
m2();
m3(); //系统自动加Review.m3()
m4();
System.out.println(i); //系统转换为System.out.println(this.i);
System.out.println(j); //系统转换为System.out.println(Review.j);
}
public static void m3(){
}
public static void m4(){
}
/*
怎么分析这个程序?
第一步:
main方法是静态的,JVM调用main方法的时候直接采用的是"类名."的方式
所以main方法中没有this
第二步:
m1()和m2()方法是实例方法,按照java语法规则来说,实例方法必须先new对象
通过"引用."的方式访问
*/
public static void main(String[] args) {
//m1(); 编译器报错
//m2();
m3(); //编译器会自动识别m3()静态方法,结果是:Review.m3()
m4();
//System.out.println(i); 保存
System.out.println(j);
//想访问m1() m2()还有i,你在static方法中只能自己new
Review r = new Review();
System.out.println(r.i);
r.m2();
r.m1();
//局部变量,局部变量在访问的时候不需要xxx.的
int k = 10000;
System.out.println(k);
}
}
class T{
//静态方法
public static void t1(){
}
public void t2(){
}
}
九. Java包(package)
Java 包(package)
为了更好地组织类,Java提供了包机制,用于区别类名的命名空间。
在Java中每定义好一个类,通过Java编译器进行编译后,都会生成一个扩展名为.class的文件。
1. 包来源
当程序的规模逐渐扩大时,就很容易发生类名称冲突,所以Java提供了一种管理类文件的机制:包
作用:包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
Java中每个接口或类都来自不同的类包,无论是Java API中的类与接口还是自定义的类与接口,都需要隶属于某一个类包,这个类包包含了一些类和接口。
为了更好的组织类,Java提供了包机制,用于区别类名的命名空间
包的本质就是一个文件夹
2. 包的作用
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
总结:Java使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
包语句的语法格式为:
package关键字,后面加包名,并且只能在第一行
package pkg1[.pkg2[.pkg3…]];
2. 完整的类路径
例如:一个程序中同时使用到java.util.Date类和java.sql.Date类,
如果在程序中不指定完整类路径,编译器不会知道这段代码使用的是java.util类包
还是java.sql类包中的Date类的完整类路径
package net.java.util
public class Something{
...
}
它的路径应该是 net/java/util/Something.java 这样保存的。 package(包)的作用是把不同的java程序分类保存,更方便的被其他java程序调用。
一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
以下是一些Java中的包:
- java.lang-打包基础的类
- java.io-包含输入输出功能的函数
注:同一个包中的类相互访问时,可以不指定包名
开发者可以自己把一组类和接口等打包,并定义自己的package。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。
由于package创建了新的命名空间(namespace),所以不会跟其他package中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。
3. 创建包
-
创建package的时候,你需要为这个package取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个package的声明放在这个源文件的开头。
-
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
-
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
注:在Java中包名设计应该与文件系统结构相对于,如一个包名为com.dzq,那么该包的类位于com文件夹下的子文件加dzq文件夹下。
没有定义包的类会被归纳到预设包(默认包)下。
4. 定义包
语法格式:package 包名
使用package关键字为类指定包名之后,包名将会成为类名中的一部分,预示着这个类必须指定全名
例如使用位于com.dzq包下的Dog.java类时,需要使用形如com.dzq.Dog这样的表达式
注:Java的包命名规则全部使用小写字母
为了避免Java中的包名冲突,通常在Java中定义包名时使用创建者的Internet域名的反序,由于Internet域名是独一无二的,包名自然不会发生冲突。
公司域名倒叙 + 项目名 + 模块名 + 功能名
一般利用公司域名倒置作为包名;
5. 导入包
在Java中,如果给出一个完整的限定名,包括包名、类名,那么Java编译器就可以很容易地定位到源代码或者类。Import语句就是用来提供一个合理的路径,使得编译器可以找到某个类。
为了能够使用某一个包的成员,我们需要在Java程序中明确导入该包。使用import语句可完成此功能。
下面的命令行将会命令编译器载入java_installation/java/io路径下的所有类
import java.io.*;
-
使用import关键字导入包
语法:
import com.dzq.*; //指定com.dzq包中的所有类在程序中都可以使用 import com.dzq.Math; //指定com.dzq包中的Math类在程序中可以使用
注意:
如果类定义中已经导入com.dzq.Math类, 在类体中再使用其他包中的Math类时就必须指定完整的带有包格式的类名, 如再使用java.lang包中的Math类,就必须使用全名java.lang.Math 当使用import指定一个包中的所有类时,并不会指定这个包的子包中的类
-
使用import导入静态成员
JDK5.0以上版本支持import关键字导入包之外,还可以导入静态成员。
语法:
import static 静态成员
实例:
分别使用import static导入java.lang.Math类中的静态成员方法max()和 java.lang.System类中的out成员变量,这时就可以在程序值直接引用这些静态成员
package com.dzq; import static java.lang.Math.max; //导入静态成员方法 import static java.lang.System.out; //导入静态成员变量 public class ImportTest { public static void main(String[] args) { //在主方法中可以直接使用这些静态成员 out.println("1和4的较大值为:"+max(1,4)); } }
5.1 import的何时使用?
A类中使用B类
- A和B类都在同一个包下,不需要import
- A和B类不再同一个包下,需要使用import
package com;
import com.pudding.javase;
public class Test02{
public static void main(String[] args){
/*
Test02在com包下
HelloWorld在com.pudding.javase包下
不再同一个package下,包名可以省略吗?
不可以省略
*/
//错误:找不到符号
/*
HelloWorld hw = new HelloWorld();
System.out.println(hw);
*/
/*
不再同一个package下,可以通过写全类名进行运行,但在这样写太复杂了
com.pudding.javase.HelloWorld hw = new com.pudding.javase.HelloWorld();
System.out.println(hw); //结果:com.pudding.javase.HelloWorld@2a84aee7
*/
HelloWorld hw = new HelloWorld();
System.out.println(hw); //结果:com.pudding.javase.HelloWorld@2a84aee7
}
}
tip:
如果当前程序中,要调用Java提供的程序,也需要先导包才可以使用;但是Java.lang包下的程序是不需要我们导包的,可以直接使用。
5.2 import怎么用?
import语句只能出现在package语句之下,class声明语句之上
import语句还可以采用* 号的方式,导入全部文件
- import java.* 是不允许的,必须到类名才行
- 因为在java语言中规定,这里的*代表某些类的名字
注意:java.lang包下的直接子类都不需要使用import导入,系统会自动进行导入。
同包下也是不需要的。
6. Java管理源文件和类文件
在Java中将Java源文件和类文件放在一起管理是极为不好的方式。
可以在编译时使用-d参数设置编译后类文件产生的位置。
例如:在CMD下进入程序所在根目录,执行
javac -d ./bin/./com/dzq/*.java
这样编译成功之后将在当前运行路径下的bin目录产生com/dzq路径,
并在该路径下出现相应的源文件的类文件
使用Eclipse编译器,并在创建项目时设置了源文件与输出文件的路径,
编译后的类文件会自动保存在输出文件的路径
7. 对应带有package的java程序怎么编译?怎么运行?
- 采用之前的编译和运行不行了
- 类名不再是:HelloWorld了
- 类名是:com.pudding.javase.HelloWorld
解决办法:
将刚刚编译的class文件放到一些新建的包目录下。下面的com包就称为了一个类
8. package的目录结构
类放在包中会有两种主要的结果:
- 包名成为类名的一部分,正如我们前面讨论的一样。
- 包名必须与相应的字节码所在的目录结构相吻合。
下面是管理你自己java中文件的一种简单方式:
将类、接口等类型的源码放在一个文件中,这个文件的名字就是这个类型的名字,并以.java作为扩展名。例如:
// 文件名 : Car.java
package vehicle;
public class Car {
// 类实现
}
接下来,把源文件放在一个目录中,这个目录要对应类所在包的名字。
....\vehicle\Car.java
现在,正确的类名和路径将会是如下样子:
-
完整类名 -> vehicle.Car
-
路径名 -> vehicle\Car.java (in windows)