计算机本身是一个存储数据(数字、单词、图片)并与设备(监视器、音响系统、打印机)交互以及执行程序的机器
计算机程序(Computer Program):是一个指令和判断序列
计算机程序会详细的告诉计算机完成一个任务所需的步骤序列
编程:是设计和实现计算机程序的行为
算法:无歧义、可执行而且将终止的一个步骤序列
伪代码:是对解决问题的步骤序列的非正式描述
要编写一个计算机程序,你需要提供CPU能够执行的一个指令序列
使用高级语言时,你只需要指定程序应当完成的动作,由编译器(compiler)将这些高级语言指令翻译成CPU所需的更详细的指令(机器指令)
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。
为了运行程序,Java编译器把源文件(source file)转换为类文件(class file),类文件包含面向对象Java虚拟机的指令。
编译器将源代码转换为虚拟机指令之后,虚拟机会执行这些指令。
执行过程中,虚拟机会访问一个预先写好的代码库,其中包括System和PrintStream类的实现,这是显示程序输出必不可少的类
一定要了解你的文件位于文件夹层次体系中的什么位置。
提交文件进行评分和建立备份副本时,这个信息至关重要。
JVM 全称 Java Virtual Machine 是java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。
也就是说.class文件并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
JRE 全称 Java Runtime Environment 是java运行时环境,这里面包含了运行java程序所需要的所有类库,一台机器上只有安装了JRE才可以运行java程序
JRE 是包含 JVM的,并且还包含了一些运行java程序所需要的类库和资源文件等。
JDK 全称 Java Development Kit 是java开发工具包,是Sun Microsystems针对Java开发员的产品。JDK 中包含了很多关于java程序开发的工具,例如编译工具javac,文档生成工具javadoc等等等等。
同理,JDK是包含JRE 和 JVM 的,并且在此基础上还包括了一些开发工具,调试工具,以及用于管理程序的管理工具等。
这里要从java的编译方式说起,java源代码编译之后并不是直接生成一个可执行文件(.exe),而是生成对应的java字节码文件(.class),这个字节码电脑的并不能运行,而是需要java虚拟机来再次进行解释,才能被cpu执行,也就是说,java程序并不是直接运行在cpu上的,而是运行在java虚拟机JVM上面的。
对于不同的从操作系统,有不同的java虚拟机。虽然是不同的虚拟机,但是他们可以识别相同的字节码文件。这样,就达到了一次编译,到处运行的目的,也就是java跨平台的原理。
编写Java程序时,应注意以下几点:
在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了
注释不会被执行,是给我们写代码的人看的
书写注释是一个非常好的习惯
public class HelloWorld {
public static void main(String[] args) {
//单行注释
//输出一个Hello,World!
System.out.println("Hello,World!");
/*
这个是多行注释!
可以注释一段文字
*/
/**
* 文档注释
* @Description HelloWrold
* @Author pudding
*/
}
}
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所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符
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
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);
}
}
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);
}
}
在实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下: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
姓名为:张三
this除了可以使用在实例方法中,还可以用在构造方法中
通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:
this(实际参数列表);
通过一个构造方法1去调用构造方法2,可以做到代码复用。
但需要注意的是:构造方法1和构造方法2, 都是在同一个类当中。
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;
}
}
super和this的对比:
this:
this能出现在实例方法和构造方法中
this语法是:“this.” 、“this()”
this不能使用在静态方法中
this.大部分情况下是可以省略的,但是在区分局部变量和实例变量的时候不能省略
public void setName(String name){
this.name = name;
}
this()只能出现在构造方法第一个行,通过当前的构造方法去调用"本类"中其他的构造方法,目的是:代码复用
super:
表示通过子类的构造方法调用父类的构造方法
模拟现实世界中的这种场景:想要有儿子,需要先有父亲
重要结论:
当一个构造方法第一行:
注意:
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类的无参数构造方法
super代表的是"当前对象(this)"的父类型特征
注意:在构造方法执行过程中一连串调用了父类的构造方法,父类构造方法又继续向下调用它的父类的构造方法,但实际上对象只创建了一个
思考: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;
}
}
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正在购物
}
}
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正在购物
}
}
通过这个测试得出结论:
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();
}
}
和属性的效果是类似的,在父和子中有同名的属性,或者说有相同的方法,如果此时想在子类中访问父中的数据,必须使用"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、C++
弱类型语言
变量可以使用前不先声明
优点:速度快例如:Python、JavaScript
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
因此Java的数据类型分为两大类
基本数据类型的存储原理:所有的简单数据类型不存在“引用”的概念,基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,而Java语言里面八种数据类型是这种存储模型;
引用类型的存储原理:引用类型继承于Object类(也是引用类型)都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的;
区别:基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
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;
}
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! 代码要精简易读
}
}
引用数据类型的理解
引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
对象、数组都是引用数据类型。
所有引用类型的默认值都是null。
一个引用变量可以用来引用与任何与之兼容的类型。
例子:Animal animal = new Animal(“giraffe”)。
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换
运算中,不同的类型的数据先转化为同一类型,然后进行运算
类型转换的举例理解
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);
}
}
类型转换的常见问题:
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);
}
}
变量是什么? 就是可以变化的量
Java是一种强类型语言。每个变量都必须声明其类型
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域
声明变量的基本格式如下:
type varName [=value] [{,varName[=value]}];
//数据类型 变量名 = 值; 可以使用逗号隔开来声明多个同类型变量
注意事项
在 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;
}
}
在类体中所定义的变量被称为成员变量,成员变量在整个类中都有效。
这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
默认初始化
数字:0 0.0
char:u000
boolean:false
引用:null
举例:
class var
{
int x=45;
static int y=90;
}
类的成员变量可分为:
y是静态变量。如果在成员变量的类型前面加上static关键字,这样的变量叫静态变量
静态变量的有效范围:
可以跨类,甚至可达到整个应用程序之内
静态变量的存取:
既可以在定义它的类内存取
还可以直接以 类名.静态变量 的方式在其他类内使用
x是实例变量
对象被称为实例
实例变量实际上就是:对象级别的变量
public class 明星类{
double height;
}
身高这个属性所有的明星对象都有,但是每一个对象都有自己的身高值。
假设创建10个明星对象,height变量应该有10份
所以这种变量被称为对象级别的变量。属于实例变量。
怎么访问实例变量?
在类的方法、构造方法或者语句块中定义的变量被称为局部变量
在类的方法中声明的变量,包括方法的参数,都属于局部变量
变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁,局部变量占用的内存是自动释放。
局部变量作用范围:
在当前定义方法的代码块中有效,不能用于类的其他方法中
局部变量必须声明才可以使用。
局部变量生命周期:
当方法被调用时,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
由于变量被定义出来之后只是暂时存在内存中,等到程序执行到某一点,该变量会被释放掉,也就是说变量有他的生命周期。
即变量的有效范围指的是程序代码能够访问该变量的区域,若超出该区域则会在编译时出现错误。
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(){
}
}
变量必须先声明,再赋值才能访问
注意:对于成员变量来说,没有手动赋值时,系统默认赋值
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
boolean | flase |
char | \u0000 |
引用数据类型 | null |
常量(Constant):初始化(initialize)后不能再改变的值!不会变的值
所谓常量可以理解为一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。
在Java中用final标志,声明方式和变量类似:
final 常量名=值;
例如:
final double PI=3.14
常量名一般使用大写字符
final修饰的变量一般添加static修饰。static final联合修饰的变量称为“常量”,常量名建议全部大写,每个单词之间采用下划线衔接。
public class Demo07 {
//static是修饰符,不存在先后顺序
static final double PI = 3.14;
public static void main(String[] args) {
System.out.println(PI);
}
}
计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:
=
+ - * / % ++ --
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);
}
}
+= -= *= /=
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
> < >= <= == != 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);
}
}
&& || !
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);
}
}
& | ^ ~ >> << >>>
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
}
}
?:
条件运算符也被称为三元运算符。该运算符有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);
}
}
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
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
当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及到运算符的优先级别的问题。在一个多运算符的表达式中,运算符优先级不同会导致最后得出的结果差别甚大。
Java语言是面向对象的程序设计语言,Java程序的基本组成单元是类,类体中又包括属性与方法。
每一个应用程序都必须包含一个main()方法,含有main()方法的类称为主类。
main()方法是类体中的主方法。
该方法的public、static、void分别是main()方法的权限修饰符、静态修饰符、返回值修饰符,Java中的main()方法必须声明为public static void。
String[] args是一个字符串类型数组,他是main()方法的参数。
main()方法是程序开始执行的位置
Java作为一种面向对象语言。支持以下基本概念:
多态
继承
封装
抽象
类:类是一个模板,它描述一类对象的行为和状态。
对象:对象是类的一个实例,有状态和行为。
例如,一条狗是一个对象,
它的状态有:颜色、名字、品种;
行为有:摇尾巴、叫、吃等。
实例
方法
消息解析
现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。
拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。
对比现实对象和软件对象,它们之间十分相似。
软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。
在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成
通过下面一个简单的类来理解下Java中类的定义:
public class Dog{
String breed;
int age;
String color;
void barking(){
}
void hungry(){
}
void sleeping(){
}
}
一个类可以包含以下类型变量:局部变量、成员变量、类变量
一个类可以拥有多个方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog类的方法。
类是一个模板,是描述共同特征的一个模板,那么共同特征包括什么?
共同特征包括:状态特征 + 动作特征
进而得出:类 = 属性 + 方法
注意:在同一个类中调用方法是可以省略前面的前缀
每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}
对象是根据类创建的。在Java中,使用关键字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
通过已创建的对象来访问成员变量和成员方法
/* 实例化对象 */
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
当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。
一个源文件中只能有一个public类
一个源文件可以有多个非public类
一个java文件里面写多个类,和在不同java文件里面写不同的单个类,然后进行编译的时候生成的class文件是相同的,也就是说也是生成不同的class文件。
源文件的名称应该和public类的类名保持一致。
例如:源文件中public类的类名是Employee,
那么源文件应该命名为Employee.java。
运行只和class文件有关系,与java文件没关系
如果一个类定义在某个包中,那么package语句应该在源文件的首行。
如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
修饰符用来定义类、方法或者变量,通常放在语句的最前端。
public class className
{
// ...
}
private boolean myFlag;
static final double weeks = 9.5;
protected static final int BOXWIDTH = 42;
public static void main(String[] arguments)
{
// 方法体
}
Java 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持4种不同的访问权限
修饰符 | 含义 | 内容 |
---|---|---|
private | 私有的 | 只能在本类中访问 |
【default】 | 默认的 | 在同一包内可见,不使用任何修饰符 |
protected | 受保护的 | 对同一包内的类 && 所有子类可见。 |
public | 共有的 | 对所有类可见 |
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。
接口里的变量都隐式声明为public static final,
而接口里的方法默认情况下访问权限为 public。
私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。
声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。
Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。
下面的类使用了私有访问修饰符:
public class Logger
{
private String format;
public String getFormat() {
return this.format;
}
public void setFormat(String format) {
this.format = format;
}
}
被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。
如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。
被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。
Protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected
但是接口的成员变量和成员方法不能声明为 protected。
子类能访问 Protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。
父类中声明为 public 的方法在子类中也必须为 public。
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public。不能声明为 private。
父类中声明为 private 的方法,不能够被继承。
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
如果这个类型的所有对象的某个属性值都是一样的,不建议定义为实例变量,浪费内存空间。
建议定义为类级别特征,定义为静态变量,在方法区中值保留一份,节省内存开销。
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;
}
}
当这个方法体当中,直接访问了实例变量,这个方法一定是实例方法
我们以后开发中,大部分情况下,如果是工具类的话,工具类当中的方法一般都是静态的(静态方法有一个优点,是不需要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(){
}
*/
}
静态变量(类变量):
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。静态变量也被称为类变量。局部变量不能被声明为static变量。
静态方法(类方法):
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
对类变量和方法的访问可以直接使用 类名.变量名 和 类名.方法名 的方式访问。
静态的变量或方法也可以通过对象名(引用).变量名|方法名访问,但是不建议这样使用,最好使用类名.变量名|方法名访问,不然会引起程序员产生困惑。
类{
//实例相关的都需要new 对象的,通过"引用. "访问
实例变量;
实例方法;
//静态相关的都是采用"类名. "访问,也可以使用"引用. ",只不过不建议
静态变量
静态方法
}
什么是静态代码块,语法是什么?
static{
java语句;
java语句;
}
static静态代码块在什么时候执行呢?
静态代码块有啥作用?
第一:静态代码块不是那么常用(不是每个类当中都需要写的东西)
静态代码块这种语法机制实际上是sun公司给我们java程序员的一个特殊的时机,这个时机叫做:类加载时机
具体的业务
项目经理说:大家把我们所写的编程代码,只要是类加载了,请记录一下类加载的日志信息
(在哪年哪月哪日几时几分几秒,那个类加载到JVM中了)
这些内容就可以写在静态代码块中
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程序
语法:
{
java语句
}
实例语句块什么时候执行?
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);
}
}
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();
}
}
用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
final修饰的类无法被继承
final class A{ //A没有子孙
}
//B类继承A类,相当于对A类的功能进行扩展。如果你不希望别人对A进行扩展
//你可以给A类加final关键字,这样的话A类就无法继承了
//错误:无法从最终A进行继承
/*
class B extends A{
}
*/
/*
错误:无法从最终String进行继承
class MyString extends String{
}
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");
}
*/
}
final修饰的局部变量,一旦赋值不能重新赋值
public static void main(String[] args) {
//局部变量
final int i = 100;
//重写赋值
//i = 200; 错误:无法为最终变量i分配值
final int m;
//第一次赋值
m = 200;
//重新赋值
//m = 300; 错误:已分配变量m
}
final修饰的引用:
/*
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修饰的变量只能赋值一次
实例变量如果没有手动赋值的话,系统会赋默认值。
实例变量什么时候赋值(初始化)
构造方法执行的过程中赋值(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;
}
用来创建抽象类和抽象方法。
抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
一个类不能同时被 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(){
.........
}
}
主要用于线程的编程。
synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。Synchronized 修饰符可以应用于四个访问修饰符。
transient 修饰符
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机 (JVM) 跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
volatile修饰符
volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。
对于一个Java程序来说,如果没有方法,会存在什么问题?
代码无法得到复用,使用该功能的时候,直接调用方法即可,这样代码得到复用
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;
}
}
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语句以;结尾
方法体当中编写的是业务逻辑代码,完成某个特定的功能
在方法体中的代码遵循自上而下的顺序依次逐行执行
在方法体中处理业务逻辑代码的时候需要数据,数据来源就是这些形参
不是一个级别。
break:跳出switch,结束循环
return 用来终止离他最近的一个方法,return之后不可以在写其他语句,因为是无效的,会报错的
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{
}
}
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;
}
}
定义:
类/接口中,用static修饰的方法。
格式:
权限修饰符 static 返回值类型 方法名(形参列表){
}
(static可与final,private共存)
特点:
调用:
继承性:
定义:
类中/接口中无static修饰的方法,类中无特殊修饰符,接口中用default修饰。
实例相关的有:实例变量、实例方法
格式:
类或者抽象类中:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
接口中:
权限修饰符 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(); // 可以通过接口的实现类对象调用,这里匿名内部类实际上是接口的实现类对象。
}
}
构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值(注意:实例变量没有手动赋值的时候,系统会赋默认值)
重点: 当一个类没有提供任何构造方法,系统会默认提供一个无参构造方法。(而这个构造方法被称为缺省构造器)
使用new运算符来调用构造方法
语法格式:
new 构造方法名(实际参数列表);
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
通常在构造方法体中给属性赋值,完成属性的初始化
}
当一个类中没有提供任何构造方法,系统会默认提供一个无参数的构造方法。这个无参的构造方法叫做缺省构造器。
当一个类中手动的提供了构造方法,那么系统将不再默认提供无参数的构造方法
无参数构造方法,和有参数的构造方法都可以调用
Student x = new Student();
Student y = new Student(123);
构造方法支持方法重载,在一个类当中构造方法可以有多个,并且所有的构造方法名字都是一样的。(参数列表不同)
构造方法:
类或者抽象类中(接口没有),与类名同名,无返回值,不能用static修饰。
格式:
权限修饰符 类名(参数) {
方法体
}
作用:
初始化实例对象。
通常会使用构造方法给一个类的实例变量赋初值,或者执行其它必要的步骤来创建一个完整的对象。
不管你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个默认构造方法,它把所有成员初始化。
调用:
通过调用构造方法,创建类的实例对象时。
new 构造方法名();
继承性:
无参构造其子类会自动调用父类的无参构造super()。含参构造不会被继承。
注意:
构造方法分为含参构造和无参构造。JAVA会在编译时自动生成无参构造,但若书写了含参构造,则必须自己写无参构造,否则只能调用含参构造。
定义:
抽象类或者接口中,用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();
}
}
// 运行结果
玩得真开心
定义:
使用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();
}
}
方法必须调用才能执行
当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
调用方法:类名.方法名(实参列表)
注意:
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;
}
}
必须先将要调用的类实例化然后通过
调用方法:对象名.方法名(实参列表)
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");
}
}
方法重载机制的缺点:
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个条件,那么我们可以认定方法和方法之间发生了重载机制
重载就是在一个类中,有相同的函数名称,但形参不同的函数
方法的重载规则:
实现理论:
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,
以选择对应的方法,如果匹配失败,则编译器报错。
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公司给我们写好的方法重载,可以打印任何类型的内容
有时候你希望运行一个程序时候在传递给他消息,这要靠传递命令行参数给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]);
}
}
}
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);
}
}
A方法调用B方法,我们很容易理解!
递归就是:A方法调用A方法!就是方法自己调用自己
利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需的多次重复计算,大大地减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合
递归没有结束条件的时候,会发生栈的内存溢出错误(Stack Over flow Error),所以递归必须要有结束条件
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));
}
}
}
在java语言中,方法调用时参数传递,和类型无关,都是将变量中保存的那个值传过去,这个值可能是一个数字123,也可能是一个java对象的内存地址0x1234
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;
}
}
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
}
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提供了包机制,用于区别类名的命名空间。
在Java中每定义好一个类,通过Java编译器进行编译后,都会生成一个扩展名为.class的文件。
当程序的规模逐渐扩大时,就很容易发生类名称冲突,所以Java提供了一种管理类文件的机制:包
作用:包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
Java中每个接口或类都来自不同的类包,无论是Java API中的类与接口还是自定义的类与接口,都需要隶属于某一个类包,这个类包包含了一些类和接口。
为了更好的组织类,Java提供了包机制,用于区别类名的命名空间
包的本质就是一个文件夹
总结:Java使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
包语句的语法格式为:
package关键字,后面加包名,并且只能在第一行
package pkg1[.pkg2[.pkg3…]];
例如:一个程序中同时使用到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中的包:
注:同一个包中的类相互访问时,可以不指定包名
开发者可以自己把一组类和接口等打包,并定义自己的package。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。
由于package创建了新的命名空间(namespace),所以不会跟其他package中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。
创建package的时候,你需要为这个package取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个package的声明放在这个源文件的开头。
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
注:在Java中包名设计应该与文件系统结构相对于,如一个包名为com.dzq,那么该包的类位于com文件夹下的子文件加dzq文件夹下。
没有定义包的类会被归纳到预设包(默认包)下。
语法格式:package 包名
使用package关键字为类指定包名之后,包名将会成为类名中的一部分,预示着这个类必须指定全名
例如使用位于com.dzq包下的Dog.java类时,需要使用形如com.dzq.Dog这样的表达式
注:Java的包命名规则全部使用小写字母
为了避免Java中的包名冲突,通常在Java中定义包名时使用创建者的Internet域名的反序,由于Internet域名是独一无二的,包名自然不会发生冲突。
公司域名倒叙 + 项目名 + 模块名 + 功能名
在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));
}
}
A类中使用B类
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
}
}
import语句只能出现在package语句之下,class声明语句之上
import语句还可以采用* 号的方式,导入全部文件
注意:java.lang包下的直接子类都不需要使用import导入,系统会自动进行导入。
同包下也是不需要的。
在Java中将Java源文件和类文件放在一起管理是极为不好的方式。
可以在编译时使用-d参数设置编译后类文件产生的位置。
例如:在CMD下进入程序所在根目录,执行
javac -d ./bin/./com/dzq/*.java
这样编译成功之后将在当前运行路径下的bin目录产生com/dzq路径,
并在该路径下出现相应的源文件的类文件
使用Eclipse编译器,并在创建项目时设置了源文件与输出文件的路径,
编译后的类文件会自动保存在输出文件的路径
类放在包中会有两种主要的结果:
下面是管理你自己java中文件的一种简单方式:
将类、接口等类型的源码放在一个文件中,这个文件的名字就是这个类型的名字,并以.java作为扩展名。例如:
// 文件名 : Car.java
package vehicle;
public class Car {
// 类实现
}
接下来,把源文件放在一个目录中,这个目录要对应类所在包的名字。
....\vehicle\Car.java
现在,正确的类名和路径将会是如下样子:
完整类名 -> vehicle.Car
路径名 -> vehicle\Car.java (in windows)