跨平台就是说,同一个软件可以在不同的操作系统(例如:Windows、Linux、mad)上执行,而不需要对软件做任务处理。即通过Java语言编写的应用程序在不同的系统平台上都可以运行。
Java拥有Java虚拟机(JVM Java Virtual Machine),即由JVM来负责Java程序在该系统中的运行。
即:JAVA语言能跨平台,但是JVM不可以跨平台。
图解:
Java Virtual Machine
简称JVM,它是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一,JVM读取并处理编译过的与平台无关的字节码(class)文件。
Jdk(java development toolkit ,java开发工具包)
是java的核心,包括了java运行环境,一堆java工具(javac、java、jdb)和java基础的类库(即javaAPI包括rt.jar)
可以认为Jre的bin目录里的就是jvm,lib中则是jvm工作所需要的类库?。
Jre(java runtime environmental,java运行时环境)
所有的java程序都要在jre下才能运行。包括jvm和java核心类库和支持文件。与jdk相比不包含开发工具(编译器、调试器、和其它工具)。
JVM(java virtual mechinal,java虚拟机)
jvm是jre的一部分,它是一个虚拟出来的计算机。Jvm的主要工作是解释自己的指令集(即字节码)并映射到本地的CPU的指令集或OS的系统调用。
简单而言:使用JDK开发完成的java程序,交给JRE去运行。
为什么JDK中包含一个JRE呢?
- 开发完的程序,总需要运行一下看看效果。
- 也是最重要的,JDK中的开发工具其实都是java语言编写的应用程序,为了方便使用才打包成exe文件。
- 如果没有JRE,那么这些工具是运行不了,因为都是使用JVM运行的。
配置技巧
在配置环境变量过程中发现,path环境变量中还有很多其他与系统相关的配置,若JDK目录更改了,这时就要再次修改path中配置的值,很容易造成误操作。
为了不因为jdk的目录或者目录名称的改变,而不断更改path的值,而导致对path变量值的误操作,可以通过以下技巧完成。
新创建一个环境变量?JAVA_HOME?记录住jdk的目录。在path中通过%%动态的获取JAVA_HOME的值即可。
例如:
JAVA_HOME=D:\Java\jdk1.8
path=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin
%path%:动态获取path环境变量的值。
%JAVA_HOME%:动态获取名称为JAVA_HOME环境变量的值。
通过javac编译工具编写如下代码:
public class Main{
public static void main(String[] args){
System.out.println("Hello World!!!");
}
}
逐一分析:
1.class :用于定义类,java语言的程序代码都需要定义在类中。
2.关键字:被java语言赋予了特殊含义的单词,例如:public static。
3.Main:为了方便使用这个类,给类自定义的类名。
4.{}:定义该类中代码的范围。
通过java Main就可以直接运行,前提是main方法,不是名字为main。
注意:一个程序的执行需要一个起始点或者入口,所以在Main类中的public static void main(String[] args){}就是这个作用。
关键字:被Java语言赋予了特定含义的一些字符!关键字中所有字母都为小写。好比汉字中的专有名词,例如:中国、北京、上海等。一般我们不能将这些词用在其他地方,改变其含义。
1. 定义数据类型:
class、interface、byte、short、int、long、float、double、char、boolean、void
2. 数据类型的值:
true、false、null
3. 流程控制:
if、else、switch、case、default、while、do、for、break、continue、return
4. 权限修饰符:
private、protected、public
5. 类、函数、变量修饰符:
abstract、final、static、 synchronized
6. 类之间关系:
extends、implements
7. 异常处理:
try、catch、finally、throw、throws
8. 包
package、import
9. 其他修饰
native(执行本地c)、strictfp(类、接口或方法上 约束表达式)、 transient(对象上不会被序列化) 、volatile(弱同步)、assert(断言)
现基础阶段用不了这么多,可以先认识一下,随后学习中将会一个个的认识。
当在定义类的时候,类中都会有相应的属性和行为。而属性和行为都是通过创建本类对象调用的。当在调用对象的某个行为时,这个行为没有访问到对象的特有数据时,调用方法而创建这个对象有些多余。可是不创建对象,行为有调用不了,这时就会想,那么我们能不能不创建对象,就可以调用行为呢?
如果创建对象调用方法,发现这个方法中没有使用到对象中的特有数据,那么创建该对象仅仅是为了调用方法,就显得这个对象创建很多余,这时可以使用static关键字修饰这个方法。
若一个方法被static关键字修饰,则该方法属于类的方法,可以通过类名的方式直接调用。
例如:
class Person {
private int age;
private String name;
Person(int age ,String name){
this.age = age;
this.name = name;
}
//说话的行为,说出自己的年龄和姓名
void speak(){
System.out.println("name="+this.name+",age="+this.age);
}
//睡觉行为,由于sleep方法没有访问对象的特有数据,可以使用静态static修饰
static void sleep(){
System.out.println("睡觉ZZZzzz....");
}
}
class PersonDemo{
public static void main(String[] args){
//sleep方法是静态方法,属于类的方法,可以使用类名直接调用
Person.sleep();
}
}
类的方法(被static修饰的方法)可以用类明 . 方法直接调用。
什么时候使用静态修饰方法呢?
答:定义功能时,如果功能不需要访问类中定义的成员变量(非静态)时,该功能就需要静态修饰。
?注意:
- 静态是随着类的加载就加载了。也是随着类的消失而消失了。
- 静态优先于对象存在,被对象共享。
- 因为静态先存在于内存中无法访问后来的对象的中的数据,所以静态无法访问非静态。而且内部无法书写this。因为这时对象有可能不存在,this没有任何指向。
- 静态方法不能访问非静态的成员。但是非静态可以访问静态成员的,静态的弊端在于访问出现局限性。好处是可以直接被类名调用。
- 静态方法中不允许出现this,super关键字。
final的意思为最终,不可变。final是个修饰符,它可以修饰类,类的成员,以及局部变量。
当程序中一个数据是固定不变的,这时为了增加阅读性,可以给该数据起个名字,为了保证这个变量的值不被修改,加上final修饰,变量就为阅读性很强的常量。
书写规范,被final修饰的常量名所有的字母都是大写的。如果由多个单词组成单词间通过 _ 连接。
final修饰类不可以被继承,但是可以继承其他类。
final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。
final修饰的变量称为常量,这些变量只能赋值一次,定义的时候必须有初始值,不能变。
final修饰的引用类型变量,表示该引用变量的引用不能变,而不是引用所指的对象中的数据还是可以变化的;
在Java程序中为了增强阅读性自定义的名称,我们就需要自定义标识符。
比如:类名,方法名,变量名等。
标识符组成:
标识符由字母、数字、下划线、$组成,不能以数字开头,注意:此处的字母还可以是中文,日文等;
标识符大小写敏感;
标识符不得使用java中的关键字和保留字;
别用Java API里面的类名作为自己的类名;
注意:在起名字的时,只要标识符不是关键字就可以了,但为了提高阅读性,经常用有意义的英文单词作为标识符。
标识符书写规范:
包名:多单词组成时所有字母都小写。xxxyyyzzz
类名接口名:多单词组成时,所有单词的首字母大写。XxxYyyZzz
变量名和函数名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接。XXX_YYY_ZZZ
变量表示内存中的一个存储区域,该区域用来存放同一类型的常量数据,并可以重复使用这个区域, 这个区域有自己的名称(变量名)和类型(数据类型)。 可以理解,变量就相当于数学中的未知数。(运行中会被改变的值。)
数据类型 变量名 = 初始化值
;( = 是赋值符号)
格式是固定的,记住它,以不变应万变。
int res = (12*24);//开辟一片名叫 res区域,存放int类型的数据。将12*24的值赋值给res区域,作为初始值。
int res1;//开辟一片名叫 res区域,存放int类型的数据。但是如果一个变量没有赋值,是无法直接使用的。
变量表示内存中的一个存储区域,这时必须给这个区域起个名字 ,才能使用。当在给变量起名时,同样需要见名知义
。同标识符。
例如:String name ;使用name表示这个空间名字,即说明这个空间存放的姓名。
注意:变量在使用时,必须先给变量空间存放数据,即
初始化
。同时变量也有自己的生命周期(作用域问题
),常量池的值有的直接用,没有就创建,注意常量池基础数据很多,开发时不考虑他的多少,优化内存问题。
static关键字不仅可以修饰方法,同时静态也可以修饰成员变量。
例如:
class Circle{
//圆的半径
private double radius;
//圆周率,由于圆周率是固定值,因此所有对象共享这个数据
//没有必要每个对象中拥有这个数据,因此可以使用静态修饰
static double pi = 3.14;
//带参数的构造函数
Circle(double radius){
this.radius = radius;
}
//获取圆面积
double getArea(){
return radius * radius * pi;
}
}
class CircleDemo {
public static void main(String[] args) {
System.out.println(new Circle(3).getArea());
}
}
如果pi这个变量没有被静态修饰的话,当创建Circle对象时,每个对象中都会有pi这个变量,但是pi是个固定不变的值,没有必要每个对象中拥有,这时可以将这个变量静态修饰,让所有对象共享就可以了。
静态变量和成员变量的区别:
- 变量所属不同
静态变量所属与类
,也称为类变量
。
成员变量所属于对象
,也称为实例变量
(对象变量)。- 内存中的位置
静态变量存储于方法区
中的静态区
中。
成员变量存储于堆内存
中。- 在内存中出现的时间
静态变量随着类的加载而加载,随着类的消失而消失。
成员变量随着对象的创建而在堆内存中出现,随着对象的消失而消失。
在生活中,使用的数据有大有小,有整数,也有小数,同时也会使用字母,或者汉字表示特定含义。在Java中根据数据的不同特点,数据也分为不同的类型。
java语言是强类型
语言,对于每一种数据都定义了明确的具体数据类型,在内存总分配了不同大小的内存空间。
整型数据根据数据范围不同,需要使用不同的关键字
来表示。
double
?和?float
关键字在内存中开辟的空间可以存放小数。double开辟8个字节的空间,float开辟4个字节的空间;
Java浮点类型常量有两种表示方式:
double
,则要声明一个常量为float
型,则需在常量后加上f 或 F,double pi = 3.14;//正确
float pi = 3.14F;//必须加上f
char用来存储通常意义上的字符,char占两个字节;范围是[0,65535],前256 (2^16)个表示?特殊字符
,通常用单引号包裹’'。
例如:
char c = 'A';
char c2 = '传';
char c3=97;
有些符号在java语言中有独特意义,当需要使用原本意义的时候需要,转义表示。
例如:键盘上的制表符tab键。可以使用?‘\t’
表示。如双引号,在Java中使用双引号表示字符串,若想使用双引号本身含义,即可使用’\”’
表示。
boolean类型通常用于逻辑运算
和程序流程控制
。
boolean 类型数据只允许取值true?或?false
boolean flag;
flag = true;
System.out.println("flag"+ flag);
String不属于基本数据类型;但和字符有一定的联系。
String是一个类
(java语言里的最小单元),表示字符串;就是一串字符,字符串用""包裹起来,连接字符用+。
String str1 = "这是一个字符串"+"这又是一个字符串";
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改
也就是说,一旦字符串确定了,就会在字符串的常量池中生成这个字符串。
String 对象是不可变的,所以可以共享
这就是说,字符串一旦生成,在字符串常量池中不可改变,那么不管有多少个引用,只要它们引用的字符串相同,就是这些引用指向同一片内存域。
String可以创建一个或两个对象
例如:
String s3 = "abc";
String s4 = new String("abc");
System.out.println(s3==s4);//false
System.out.println(s3.equals(s4));//true,
在s4创建时,内存中有两个对象,一个是new的对象在堆中,另一个时字符串本身对象在字符串常量池中。
String里的方法海了去了,全记住不太现实,挑挑拣拣来几个,品一下String这小子差不多都能干嘛!
// 1.有多少个字符
String str = "abada";
int len = str.length();
System.out.println("len="+len);
// 2.字符的位置
str = "sdfghjkl;wertyuiop[sdfghjkla;";
int index = str.indexOf('a');//获取的是a字符第一次出现的位置
System.out.println("index="+index);
int index1 = str.indexOf('a',index+1);//第二个a字母出现的位置
System.out.println("index1="+index1);
int index3 = str.lastIndexOf('m');//如果要找的字符不存在,返回-1
System.out.println("index3="+index3);
// 3.获取指定位置上的字符
str = "itcast";
char ch = str.charAt(3);//不存在角标会发生StringIndexOutOfBoundsException
System.out.println("ch="+ch);
// 4.获取部分字符串
String s = str.substring(2, 4);//包含头,不包含尾。
System.out.println("s="+s);
//5、字符串是否以指定字符串开头。结尾同理。
//boolean startsWith(string)
//boolean endsWith(string)
String str = "StringDemo.java";
boolean b1 = str.startsWith("Demo");//返回值为boolean类型
//6.字符串中是否包含另一个字符串。
//boolean contains(string);
boolean b2 = str.contains("Demo");
// 7、字符串中另一个字符串出现的位置。
//int indexOf(string)
int index = str.indexOf("Demo");//没有返回-1
System.out.println(index)
// 8、将字符串中指定的字符串替换成另一个字符串。
//String replace(oldstring , newstring)
String s = str.replace("haha", "Test");//没有被替换内容时,结果是原串。
System.out.println("s="+s);
// 9、将字符串转成一个字符数组。或者字节数组
char[] chs = str.toCharArray();
byte[] bytes = str.getBytes();
// 10、将字母字符串转成大写的字母字符串。
String upperString = str.toUpperCase();
System.out.println(upperString );
//11、字符串按照指定的方式分解成多个字符串, "lisi,wangwu,zhaoliu"获取三个姓名。
str = "lisi,wangwu,zhaoliu";
String[] names = str.split(",");
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
// 12、字符串如何比较大小
int result = "ab".compareTo("ab");
System.out.println("result:"+result);
StringBuffer是一个线程安全
的可变字符序列。一个类似于String的字符串缓冲区,不能修改,但通过某些方法调用可以改变该序列的长度和内容。
1、StringBuffer基本使用
StringBuffer的特点:
1、是一个字符串缓冲区,其实就是一个容器。
2、长度是可变,任意类型都行。注意:是将任意数据都转成字符串进行存储
。
3、容器对象提供很多对容器中数据的操作功能,比如:添加,删除,查找,修改。
4、所有的数据最终变成一个字符串。
例如:
//创建一个字符串缓冲区对象。用于存储数据。
StringBuffer sb = new StringBuffer();
//添加数据。不断的添加数据后,要对缓冲区的最后的数据进行操作,必须转成字符串才可以。
String str = sb.append(true).append("hehe").toString();
sb.append("haha"); //添加字符串
sb.insert(2, "it");//在指定位置插入
sb.delete(1, 4);//删除
sb.replace(1, 4, "cast");
StringBuffer和StringBuilder区别:
StringBuilder:该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快,但是它线程不安全
。
StringBuffer:是线程安全
的可变字符序列,是一个容器,容器中可以装很多字符。并且能够对其中的字符进行各种操作。
当在存储整数数据时,Java中所有的 整数默认 都是int
类型的。所以在将-128~127之间的数据存储在byte
类型空间中时,JVM会把这个int类型的数据自动转换成byte类型,然后存放在byte空间中;short
同理。
// 类型转化
byte b = 2;// 隐式转化 int类型的2转化为byte类型 放在b中
short s = 34;// 同上
char ch1 = 65;// 隐式转化 int类型的65转化为char类型 放在ch1中
char ch2 = 'A';//直接将A字符存放在ch2空间中
// 当数据超过需要隐式转化的类型时,会发生错误。
// char ch = 65536;
// byte b2 = 128;
// short s2 = 65536;
基本数据类型转化的范围:byte,short,char → int → long → float → double
整型,字符型,浮点型的数据在混合运算中相互转换,转换时遵循以下原则:容量小的类型可自动转换为容量大的数据类型(类型自动提升):
byte,short,char → int → long → float → double
byte,short,char
之间不会相互转换,他们在计算时首先会转换为int类型。
容量大的数据类型转换为容量小的数据类型时,要加上强制转换符,但可能造成精度的降低或溢出,使用时要格外注意。
强制转换格式:(数据类型)(被转数据);
long l = 123L;
int i = (int) l;
double d = 3.14;
float f = (float) d;
Java中的算术运算符和生活中的算数运算基本相似。
在使用算术运算时需要注意运算两侧类型:当两侧类型不统一时,运算结果和较大类型保持一致。
自加自减注意:
当自加符号在变量右侧
时,需要先将变量的值临时保存,然后给变量空间加1,接着用临时变量空间中的值去和其他运算符进行运算。(需要开辟新的空间)
当自加符号在变量左侧
时,需要先给变量空间加1,然后把加1后的值和其他运算符进行运算。(不需要开辟新的空间)
赋值运算符号:
=、+=、-=、/=、%=
赋值号的功能是将赋值号右侧的结果存储到左侧的变量空间中。
赋值号和算术运算符组合起来的混合运算符,它们即具有算术运算的特定,同时还具有赋值运算的特定。
int a = 4;
a += 3; //相当于 a = a + 3;的运算,即把a空间中的值和3进行加法运算,把和值从新存放到a空间中
比较运算符符号:
>:大于?
>=:大于等于?
<:小于
<=:小于等于
==:相等
!=:不等
比较运算符。运算完的结果要么true,要么false。后期学习中会根据比较的结果做出相应逻辑判断。
逻辑运算符。用来连接boolean型表达式的。
& : 与
| :或
^ : 异或。
!:非
&& 短路与 || 短路或
需求1:最有效率运算2乘以8.通过位移运算。2<<3
需求2,对两个整数变量的值进行互换(不需要第三方变量)
三元置换方法:
//通过第三方变量的形式进行置换。
int a = 3, b = 7;
int temp = a;
a = b;
b = temp;
//通过和的形式。有个弊端,两个数据较大,可能会超出int范围。
a = a + b;//a = 3 + 7;
b = a - b;//b = 3 + 7 - 7; b = 3;
a = a - b;//a = 3 + 7 - 3; a = 7;
//技巧。异或。
a = a ^ b;// a = 3 ^ 7;
b = a ^ b;// b = 3 ^ 7 ^ 7;
a = a ^ b;// a = 3 ^ 7 ^ 3;
流程:
程序的流程即就是代码的执行顺序。 分为循环
和判断
两个方面。
例如:
public class Main{
public static void main(String[] args) {
int a = 3 ;
int b = 5 ;
int sum ;
sum = a + b;
System.out.println("sum="+sum);
}
}
上述代码当在dos命令行中写入java Main
回车之后,首先会启动JVM,JVM就会去加载当前Main这个class文件,并执行其中的main方法。当程序执行main方法的时候会从第一行开始往下执行,直到整个代码全部执行完成。在此过程中程序按照书写的顺序,不会跳过任何一行代码。像这样的执行流程就是常见的顺序执行结构。
if(条件表达式){
执行语句;
}
if(条件表达式){
????????执行语句;
}
else {
????????执行语句;
}
if(条件表达式){
????????执行语句;
}else if (条件表达式){
????????执行语句;
}
…… else{
????????执行语句;
}
代码演示:
class IfTest {
public static void main(String[] args) {
//1,定义变量。记录数据。
int week = 9;
//2,通过判断结构语句if对该变量进行判断。
if(week == 1)
//3,根据不同的条件,通过输出语句显示不同的结果。
System.out.println(week+"对应的是星期一");
else if(week == 2)
System.out.println(week+"对应的是星期二");
else if(week == 3)
System.out.println(week+"对应的是星期三");
else if(week == 4)
System.out.println(week+"对应的是星期四");
else if(week == 5)
System.out.println(week+"对应的是星期五");
else if(week == 6)
System.out.println(week+"对应的是星期六");
else if(week == 7)
System.out.println(week+"对应的是星期日");
else
System.out.println(week+"没有对应的星期");
}
}
switch(表达式){
case 取值1:
????????执行语句;
break;
case 取值2:
????????执行语句;
break;
…
default:
????????执行语句;
break;
}
程序执行时,遇到switch
关键字,首先会计算表达式的值,然后根据计算的值和case
后面的值做比较,当case后面的值和switch表达式的值相同时,就执行case身后的所有语句,若case身后没有和switch表达式匹配的值,程序就会执行default
后面的语句。
switch语句选择的类型只有四种:byte,short,int , char(java5之前),还支持 enum(jdk5), String(jdk7);
经典实例代码:
public class Score {
public static void main(String[] args) {
int res = 76;
switch(res/10){
case 10:
case 9:
System.out.println("成绩为: A");
break;
case 8:
System.out.println("成绩为: B");
break;
case 7:
System.out.println("成绩为: C");
break;
case 6:
System.out.println("成绩为: D");
break;
default:
System.out.println("成绩为: E");
break;
}
}
}
if可以用于判断数值,也可以判断区间,只要运算结果是boolean类型,都可以进行判断。
switch用于对固定的几个值,进行判断。判断的值的类型有限。
循环结构
根据程序的需求,可以将某些代码重复执行的操作。Java中的循环结构有如下三种:
while:事先不需要知道循环执行多少次;
do-while:同上,只是至少要执行一次(先做,后判断);
for:需要知道循环次数;
while(条件表达式){
执行语句;
}
while执行顺序:当程序遇到while关键字时,JVM首先会运算while后面的条件表达式,当条件表达式为true时,就会执行while后面大括号中的语句,当把大括号中的所有语句执行完之后,会又回到while的条件表达式处再次判断,若还为true,就继续执行大括号中的语句,若为false就跳过大括号中的所有语句,继续往下执行。
示例:
public class Add{
public Static void main(String[] args){
int i = 1;
int sum = 0;//2,需要循环结构。
while(i<=10){ //3,循环中需要进行和+下一个数的运算。并让下一个数自增。
sum = sum + i;
i++;
}
System.out.println("sum="+sum);
}
}
注意事项
do{
执行语句;
}while(条件表达式);
while和do-while对比
用代码说明问题:
int x = 3;
while(x<3)
{
System.out.println("x="+x);
x++;
}
System.out.printin("===============================")
int y = 3;
do
{
System.out.println("y="+y);
y++;
}while (y<3);
while先判断条件再循环;
do-while是无论条件是否满足,循环体至少执行一次。
for(初始化表达式(1);循环条件表达式(2);循环后的操作表达式(3)){
执行语句;(4)
}
for里面的3个表达式运行的顺序,初始化表达式只读一次,判断循环条件,为真就执行循环体,然后再执行循环后的操作表达式,接着继续判断循环条件,重复找个过程,直到条件不满足为止。
编号表示:1》》》2》》》4》》》3·········2(当判断结果false时)。
代码举例:
class ForTest {
public static void main(String[] args) {
//1,定义变量,记录个数。
int count = 0;
for (int x = 1; x<=100 ; x++ )
{
//对数值进行判断,是否是6的倍数。
if(x % 6 == 0)
count++;
}
System.out.println("count="+count);
}
}
for和while的区别:while与for可以互换,区别在于for为了循环而定义的变量在for循环结束就在内存中释放。而while循环使用的变量在循环结束后还可以继续使用。
嵌套循环练习:
class ForForTest {
public static void main(String[] args) {
for (int x=1; x<=5 ; x++){
for (int y=1 ;y<=x ;y++ ){
System.out.print("*");
}
System.out.println();
}
}
}
break:终止该层循环;
continue:跳过该次循环
举例:
//break是终止循环,即在程序中遇到break,那么break所属的循环将结束。
for (int x = 0; x < 3; x++) {
if (x == 1) {
break;
}
System.out.println("x=" + x);
}
//continue是结束本次循环,继续下次循环。循环是不会结束的。
for (int x = 0; x < 10; x++) {
if (x % 2 == 0) {
continue;
}
System.out.println("x=" + x);
}
outer:for (int x=0; x<3 ;x++ ){
inner:for (int y=0; y<4 ;y++ )
{
System.out.println("x="+x);
break outer;
}
}
System.out.println("========================================");
outer:for (int x=0; x<3 ;x++ ){
inner:for (int y=0; y<4 ;y++ )
{
System.out.println("x="+x);
continue outer;
}
}
变量空间是可以存放数据的,但是每个变量空间只能存放一个数据,数组是同一种类型数据的集合;即能够存放多个相同类型的数据的容器。
数组是存放多个数据的容器,Java中需要使用new关键字来创建容器,在创建容器时也要明确容器的大小,即容器中存放数据个数。有以下格式:
元素类型[] 数组名 = new 元素类型[元素个数或数组长度]
元素类型 数组名[] = new 元素类型[]{元素1,元素2,....元素n};
元素类型 数组名[] = {元素1,元素2,....元素n};
public static void main(String[] args)
{
int nums[]=new int[3];//定义一维数组
System.out.println(nums);
int length = ns.length;//计算数组长度
System.out.println(length);
int nums2[] = {1,2,3,4};//简化定义数组
System.out.println(Arrays.toString(nums2));//打印数组,相当于数组转字符串
int nums3[] = {1,2,3,4};
System.out.println(nums3[2]);//打印数组中第n个元素(从第0个算起)
}
选择排序是排序中的一种。算法原则(从小到大排序
):先用数组中第一个空间值和数组中剩余的其他空间值 依次做比较,在比较的过程有任何值比第一个空间值小,就用第一个空间
值和当前这个空间
值换位置,直到所有值和第一个空间值全部比较完。第一个空间
中就放着最小值
。接着在使用数组第二个空间值和数组中剩下空间中值做比较,比较方式和前一次相同。以此类推,比较完成。
规律:第一个空间
和剩余空间比一圈,第二个空间
在和剩余空间比一圈,以此类推,到倒数第二个空间值和最后一个空间值比完,整个数组有有序了。正好符合了外循环循环一次,内循环执行一圈。即外循环控制每次到底使用哪个空间值和剩余空间比较,内循环提供剩余空间。
图解:
//利用for语句选择排序
for (int i=0;i<ns.length-1;i++){
for (int j=i+1;j<ns.length;j++){
//一个个进行比较
if (ns[i]>ns[j]){
int temp;//三元置换
temp=ns[j];
ns[j]=ns[i];
ns[i]=temp;
}
}
}
第一个
空间值和第二个
空间值比较,把较大的值存在第二个空间中,接着第二个空间
值和第三个空间
值作比较,较大的值存在第三个空间中,以此类推直到倒数第二个空间
和倒数第一个
空间比较,把较大值存放在最后一个空间中。最大值
了,那么第二轮算法和第一轮一样,只是最后一次比较是倒数第三个空间和倒数第二个空间比较。把较大的值存在倒数第二个
空间中。 for (int i=0;i<nums.length-1;i++)//利用for语句冒泡排序
{
for (int j=0;j<nums.length-1-i;j++)//缩小比较长度
{
if (nums[j]>nums[j+1])//两两比较
{
int temp=nums[j+1];//三元置换
nums[j+1]=nums[j];
nums[j]=temp;
}
}
}
int[][] arr = new int[3][2];
方法(函数)就是定义在类中的具有特定功能的一段独立小程序,并能被多次使用。
例如: 站在代码复用的角度来考虑,之前的打印 “*” 三角形问题。
问题:要画矩形,需要不断使用该for嵌套代码。造成代码复用性很差。
解决:定义一个功能用于画矩形,每次只要使用该功能即可。这样提高复用性。
public static void draw(int row,int col){
for(int x=0; x<row; x++)
{
for(int y=0; y<col; y++)
{
System.out.print("*");
}
System.out.println();
}
//return;
}
修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,...){
执行语句;
return 返回值;//return关键字是用于结束该功能。并将后面的具结果返回给调用者。
//必须有return语句。
}
class MethodDemo
{
public static void main(String[] args)
int a = 3;
int b = 4;
int sum = getSum(a,b);
System.out.println("sum="+sum);
}
public static int getSum(int a , int b)
{
return a + b;
}
}
说明:
- 首先会加载main方法加载进栈内存中,并执行main方法中的代码,分别给a变量开辟空间并存放3,给b变量开辟空间存放4。
- 当程序执行到 int sum = getSum(a,b)时;会将getSum函数加载进栈内存中,同样在getSum所属的栈区域中开辟a和b变量空间,接受main方法中的getSum(a,b)传递的值。
- 然后执行getSum函数中的代码,当getSum函数执行结束后,函数会出栈(弹栈)。
- 程序回到main方法的调用语句int sum = getSum(a,b);处,并将getSum方法执行完返回的结果赋值给sum变量,程序继续往下执行。打印sum的值。
Java为我们提供了重载这个功能,使用重载就可以解决某个功能不同场合的泛用性,一个函数的多个版本。
两个数相加,需要一个函数名字getSum,三个数相加需要一个函数名字getSum2,那如果有100个数相加,不是就需要getSum100了?那是不是在调用函数的时候还需要去记住哪个函数名对应多少个参数?这很显然是不可能的了。这个时候就要用重载
。
在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可,这时就形成了重载。重载和返回值类型无关。
class MethodDemo3
{
public static void main(String[] args)
{
printCFB();
}
//打印标准九九乘法表。
public static void printCFB()
{
printCFB(9);
}
//打印任意乘法表。
public static void printCFB(int num)
{
for(int x=1; x<=num; x++)
{
for(int y=1; y<=x; y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}
}
在定义函数时,函数名不能使用关键字,第一个单词的首字母小写,采用驼峰式;由有实际意义的动词或动词短语。如:getSum。
public class Demo07 {
// 方法{}里的内容为方法体
public static int computer(int a,int b){
return a+b;
}
public static int computer(int a, int b ,int c){
return a+b+c;
}
public static double computer(double a, double b){
return a/b;
}
// public static double computer(double num01, double num02){
// return 0;
// } 函数的参数是一种形式参数
// public static int computer(double a, double b){
// return (int)a/b;
// }
public static void main(String[] args) {
computer(2.3,1.7); // JVM不知道调用结果的 如果重载跟返回值有关 调用时就会产生歧义
computer(1.0,2.0); // 参数名也是不会认为是参数列表的属性,名称只是形式,所以参数列表里的参数名全是形式参数
}
}
1. 寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.
2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。)
3. 堆:存放所有new出来的对象。
4. 静态域:存放静态成员(static定义的)
5. 常量池:存放字符串常量和基本类型常量(public static final)
6. 非RAM存储:硬盘等永久存储空间
栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
垃圾回收站(GC):会检查托管堆中是否有应用程序不再使用的任何对象。如果有,他们的内存就可以回收。
1.垃圾回收器开始执行时,垃圾回收器沿着线程栈上行以检查所有根(然后遍历所有类型对象上的所有根),如果发现一个根引用了一个对象,就在对象的“同步块索引字段”上开启一个位(对象就是这样标记的),如果对象里又引用有其他对象,则会遍历所有对象以标识。检查好所有根之后,堆中将包含一组已标记和未标记的对象。
2.垃圾回收器线性遍历堆,以寻找未标记对象的连续内存。如果发现的内存块比较小,垃圾回收器会忽略它们。但是,如果发现大的、可用的连续内存块,“垃圾回收器会把非垃圾的对象移动到这里以压缩堆”。
最后记得重新分配地址存根