jvm:java虚拟机
加载并运行.class
jre:java运行时环境
除了包含jvm以外还包含运行java程序所必须的环境变量
jre=jvm+系统类库
jdk:java开发工具包
除了包含jre以外还包含开发java程序所必须的命令
jdk=jre+编译、运行等开发工具
java语言规定变量在使用之前必须初始化,即必须给该变量赋予特定的值。
精确运算场合不能使用double和float
boolean 要么是true,要么是false
单引号是char
byte,short,char型变量参与运算时,先一律转为int再运算。
Scanner的用法
import java.util.Scanner;
Scanner scan = new Scanner(System.in);
扩展赋值运算符不用强制转换类型,系统自动转换
例子1:
byte a = 55;
a += 10; // 自动转换成byte赋值
System.out.println(a);
例子2:
byte a = 55;
a = a + 10; // 会报错
Syste.out.println(a);
字符串拼接:
若两边为数字,则做加法运算
若有一边为字符串,则做字符串拼接
switch 语句
public class demo5 {
public static void main(String[] args) {
int num = 1;
switch(num) {
case 1:
System.out.println("one");break;
case 2:
System.out.println("two");break;
case 3:
System.out.println("three");break;
case 4:
System.out.println("four");break;
case 5:
System.out.println("five");break;
default:
System.out.println("default way");
}
}
}
print\println\printf的区别
print 将它的参数显示在命令窗口,并将输出光标定位在所显示的最后一个字符之后。
println 将它的参数显示在命令窗口,并在结尾加上换行符,将输出光标定位在下一行的开始。
printf 是格式化输出的形式。
数组
- 数组的赋值
int[] arr1 = new int[4];
int[] arr2 = {1,4,7};
int[] arr3 = new int[]{1,4,7};
int[] arr4; arr4 = new int[] {1,4,7};
int[] arr5; arr5 = {1,4,7}; //这个会编译错误,这种方式只能同时声明
- 数组的复制,要避免数组下标越界
int[] a = {10, 20, 30, 40, 50};
int[] a1 = new int[6];
System.arraycopy(a, 0, a1, 1, 4);
for(int i=0; i<a1.length; i++) {
System.out.print(a1[i] + ", ");
}
打印结果:0, 10, 20, 30, 40, 0,
- 数组的扩容
数组的长度在创建后是不可改变的,所谓的扩容是指创建一个更大的新数组并将原有数组的内容复制到其中。
扩容之后如果增加的区间没有赋值,会默认为0
import java.util.Arrays;
public class demo2 {
public static void main(String[] args){
int[] a = new int[6];
for(int i=0; i<a.length; i++) {
a[i] = (int)(Math.random()*100);
}
int[] b = Arrays.copyOf(a, a.length + 1);
for(int i=0; i<b.length; i++) {
System.out.print(b[i] + ", ");
}
}
}
打印结果:18, 44, 32, 47, 13, 27, 0,
- 数组的排序,Arrays.sort() 是Java内置的升序函数,这种方法效率高,速度快
import java.util.Arrays;
public class demo7 {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i=0; i<10; i++) {
arr[i] = (int)(Math.random()*100);
}
Arrays.sort(arr);
for(int i=0; i<10; i++) {
System.out.print(arr[i]+"\t");
}
}
}
打印结果:
6 17 36 45 63 65 67 76 78 82
- 冒泡排序,冒泡的工作原理
1.四个数冒三轮
2.每一轮都是从第一个元素开始冒,每一次都是和它的下一个元素比
3.冒出来的数就不比较了
public class demo8 {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i=0; i<arr.length; i++) {
arr[i] = (int)(Math.random()*100);
}
for(int i=0; i<arr.length-1; i++) {
for(int j=0; j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]) {
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
for(int i=0; i<arr.length; i++) {
System.out.print(arr[i]+"\t");
}
}
}
打印:
4 18 21 27 58 59 59 61 62 99
类和对象
类和对象
类是对象的模版,对象是类的具体的实例。
class 类名{
成员变量 有默认值
}
Student li = new Student()
Student 是类型, li是引用类型变量,称为引用, = 是指向, new Student() 是对象
堆中放置对象(数组也是对象)和成员变量,栈中放置局部变量、基本类型和对象的地址指向
引用类型之间画等号,是指向地址,修改其中一个会影响另一个
基本类型之间画等号,是赋值(int a=5;int b=a;a与b是赋值关系)
对于引用类型变量,可以对其赋值为null,表示没有指向任何对象
不能对null做操作,若操作则会报空指针异常NullPointerException
java 默认堆大小是物理内存的1/4
默认栈大小是1m
java VM调整堆大小
java -X 查看java非标准输出帮助
java -Xmx100m 设置jvm的堆空间最大值
java -Xms 设置jvm的堆空间初始值
java -Xss1m 设置栈大小
方法,重载,构造方法,this指代当前对象
1. 同一个文件中可以包含多个类
2. public修饰的类只能有一个
3. public修饰的类必须与文件名相同
方法的签名:根据方法名和参数列表[与返回值类型无关],与[参数名称无关]
方法的重载:与返回类型无关,只看参数列表
1. 在java语言中,允许多个方法的名称相同,但参数列表不同,称之为方法的重载(overload)
2. 根据签名来调用方法
编译器在编译时会根据签名来绑定调用不同的方法,我们可以把重载的方法看成是完全不同的方法,
只不过恰好方法名相同而已
构造方法/构造器/构造函数
1. 常常用于给成员变量赋初值
2. 与类同名,没有返回类型
3. 在创建对象(new)时被自动调用
4. 若不写构造方法,系统默认提供无参构造方法,若写了,则不再默认提供
5. 构造方法可以重载
public class demo4 {
public static void main(String[] args){
Dog dog = new Dog();
System.out.println("ending");
}
}
class Animal{
int a;
Animal(){
System.out.println("animal construtor");
}
}
class Dog extends Animal{
int b;
Dog(){
System.out.println("dog constructor");
}
}
打印结果:
animal construtor 执行子类的构造方法之前先执行父类的构造方法,有了父类,才有子类
dog constructor
ending
this:指代当前对象,哪个对象调指的就是哪个对象
用法:
1. this.成员变量名
2. this.方法名
3. this() 调用构造方法
class Cell{
int row;
int col;
Cell(int row,int col){ 局部变量和成员变量同名的时候,加this指代当前对象
this.row = row;
this.col = col;
}
void drop() {
row++;
}
}
数组是对象
1. 在java中,数组属于引用类型数据
2. 数组对象在堆中存储,数组变量属于引用类型,存储数组
对象的地址信息,指向数组对象
3. 数组的元素可以看成数组对象的成员变量(只不过类型全都相同)
引用类型数组,默认值为null
Cell[] cells = new Cell();
声明int[]型数组arr,包含3个元素
每个元素都是int[]型,默认值为null
int[][] arr = new int[3][];
int[][] arr = new int[3][4];
内存机制,成员变量,super方法
对象内存管理
1. 编译好的java程序需要运行在JVM中,无论代码还是数据,都需要存储在内存中
2. JVM 为java程序提供并管理所需要的内存空间
3. JVM内存分为“堆”、“栈”和“方法区”三个区域,分别用于存储不同的数据
note.png
自动垃圾回收-----GC垃圾回收器
1. 垃圾回收器(Grabage Collection, GC)是JVM自带的一个线程(自动运行着的程序),
用于回收没有任何引用指向的对象。
2. java程序不用担心内存管理,因为垃圾收集器会自动进行回收管理。
内存管理:由JVM来管理
堆:
1. 用于存储所有new出来的对象(包括成员变量)
2. 成员变量的生命周期:
创建对象时存在堆中,对象被GC回收时一并清空
垃圾:没有任何引用所指向的对象
3. 垃圾回收器(GC)不定时到堆中查看,自动回收垃圾
回收过程时透明的,并不是一看到垃圾就马上回收,若想让它快一些收,可以调用System.gc()
4. 内存泄漏:不再使用的对象没有被及时的回收
建议:当对象不再使用时应及时将其赋为null
栈:
1. 用于存储正在调用中的方法的所有局部变量(包括参数)。
2. 调用方法时在栈中为该方法分配一块对应的栈帧。
栈帧中包含所有的局部变量(包括参数),
方法调用结束时,栈帧消失,局部变量一并消失了
3. 局部变量的生命周期:
调用方法时存在栈中,方法调用结束时消失
方法区:
1. 存放.class字节码文件(包括方法)
2. 方法只加载一份,通过this来区分具体哪个引用
每个类实例出来的对象有自己对应的变量,所有对象共享同个类
成员变量
1. 类中,方法外
2. new时
class Aoo {
int a;
void test() {
int b;
System.out.println(a); 成员变量有默认初值
System.out.println(b); 编译错误,局部变量b没有默认初值
}
}
成员对象的生命周期
1. 访问对象需要依靠引用变量。
2. 当一个对象没有任何引用时,被视为废弃的对象,属于被回收的范围。
该对象中的所有成员变量也随之被回收。
3. 成员变量的生命周期:从对象在堆中创建开始到对象从堆中被回收结束。
继承 --- 单一继承
1. 目的:避免代码重复,有利于代码的重用
2. 通过extends实现继承
3. 父类:所有子类所有共有的属性和行为
子类:子类所特有的属性和行为
4. 一个父类可以有多个子类
一个子类只能有一个父类----单一继承
5. 继承具有传递性
6. 泛化和继承是一个意思
7. java规定:构造子类之前必须先构造父类,子类构造中若没调用父类的构造方法,
则默认super()来调用父类的无参构造,若自己调用了,则不再默认提供,
super()调用父类构造,必须位于子类构造的第一句
super:指代当前对象的父类对象
用法:
super.成员变量名----访问父类的成员变量
super.方法名()------调用父类的方法
super()-------------调用父类的构造方法
class Foo{
int a;
Foo(int b){
System.out.println(b);
}
}
class Goo extends Foo{
int a;
Goo(){
super(66); 不写则默认调用super(),而父类中没有无参数构造方法,就会报错
System.out.println("hello guys");
}
}
对于程序来讲,要考虑扩展性
向上造型,也就是多态
1. 父类型的引用指向子类的对象
2. 能点出来什么,看引用类型
public class Demo5 {
public static void main(String[] args) {
Animal dog = new Dog();
dog.eat();
-----dog.barking(); dog没有barking属性,依据的是引用Animal类型
}
}
class Animal{
int a;
void eat(){
System.out.println("Animal eat");
}
}
class Dog extends Animal{
int d;
void barking() {
System.out.println("dog barking!!");
}
}
方法的重写(Override)
1. 发生在父子类中,方法名相同,参数列表相同,也就是签名相同,但是方法的实现不同
2. 重写方法被调用时,首先调用子类的重写的方法
重载看引用,重写看对象,也就是调用方法时首先去找子类中找
重写与重载的区别
重写:父子类中,方法名相同,参数列表相同,方法体不同
遵循"运行期"绑定,根据对象类型调用方法
重载;一个类中,方法名相同,参数列表不同,方法体不同
遵循"编译器"绑定,根据引用类型绑定方法
重写需要遵循"两同两小一大"原则:一般都是一模一样的
1. 两同
方法名称相同,参数列表相同
2. 两小
子类方法的返回值类型小于或等于父类的
void和基本类型时,必须相同
引用类型时,小于或等于(父类大,子类小)
子类方法抛出的异常小于或等于父类的------异常处理
3. 一大
子类方法的访问权限大于或等于父类的------访问修饰符
##1,这是重写方法的调用
public class demo5 {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.eat();
Animal dog2 = new Dog();
dog2.eat();
}
}
class Animal{
int a;
void eat(){
System.out.println("Animal eat");
}
}
class Dog extends Animal{
int b;
void eat() {
System.out.println("dog eat!!");
}
}
打印结果:
dog eat!!
dog eat!! 不管引用类型为如何,重写方法的调用首先都是从子类中开始,
##2.如果不为重写方法调用呢?
public class demo5 {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.bark();
Animal dog2 = new Dog();
dog2.bark(); 这句就会报错了,因为Animal中找不到bark方法,而bark方法是子类所特有的,
可以看出,寻找非重写方法的是从引用类型中去找的
}
}
class Animal{
int a;
void eat(){
System.out.println("Animal eat");
}
}
class Dog extends Animal{
int b;
void bark() {
System.out.println("dog bark!!");
}
}
包,package
1. 作用:避免类的命名冲突
2. 包名可以有层次结构
3. 建议:包名所有字母都小写
4. 类的完全限定名:包名.类名
import
1. 作用:声明类/引入类
2. 同包中的类可以直接访问,不同包中的类想访问,先import声明类再访问
建议:数据私有化(private),行为公开化(public)
访问控制修饰符:
1. public:公开的,任何类
2. private:私有的,本类
3. protected:受保护的,本类、子类,同包类
4. default:默认的,什么都不写,本类、同包类
类的访问修饰符:public和默认的
类成员的访问修饰符:如上四种都可以
项目非常大的时候,建议:域名反写.项目名称.模块名称.类名
static(静态)
1. 静态变量
2. 静态方法
3. 静态块
成员变量
1. 实例变量:
属于对象的,存在堆中
有几个对象就有几个实例变量
必须通过对象名.来访问
2. 静态变量:
属于类的,存在方法区中
只有一份,
常常通过类名.变量名来访问,也可以用对象名.变量名访问
何时用:所有对象的数据都一样时使用
class Loo{
int a; 实例变量
static int b; 静态变量
void show(){ 实例方法
a++;
b++;
}
static void test(){ 静态方法
a++; 编译错误,没有隐式的this,而a必须通过this.来访问,
b++; 所以此处不能访问a
}
}
没有this就意味着没有对象,而实例变量必须通过对象点来访问,
所以静态方法中不能直接访问实例成员
静态方法
1. 由static修饰
2. 属于类的,存在方法区中,只有一份
3. 常常通过类名.来访问
4. 没有隐式的this传递
静态方法不能直接访问实例成员
5. 何时用:方法的操作仅与参数相关而与对象无关
public class Demo2 {
public static void main(String[] args) {
Foo foo1 = new Foo();
foo1.test();
foo1.show();
Foo foo2 = new Foo();
foo2.test();
foo2.show();
}
}
class Foo{
int a=0;
static int b=0;
void test(){
a++;
b++;
}
void show() {
System.out.println(a+", "+b);;
}
}
打印结果: 静态方法,静态变量只存储一份,所以会存储上次修改
1, 1
1, 2
静态块
1. 由static修饰
2. 属于类的,类被加载期间自动执行,
类只被加载一次,所以静态块也执行一次
3. 何时用:常常用于加载静态资源(图片、音频、视频。。。)
public class Demo3 {
public static void main(String[] args) {
Aoo foo1 = new Aoo();
Aoo foo2 = new Aoo();
}
}
class Aoo{
static {
System.out.println("静态块");
}
Aoo(){
System.out.println("构造方法");
}
}
打印结果: 静态块只会被执行一次,在类被加载进方法区时执行一次,
静态块 适合用于加载静态资源(不变共享的资源)
构造方法
构造方法
final(不能修改的)
1. 修饰变量:变量不能被改变
2. 修饰方法:方法不能被重写
3. 修饰类:类不能被继承
修饰局部变量时,用之前初始化即可
final修饰成员变量,两种方式初始化:
1. 声明的同时初始化
2. 构造方法中初始化
final修饰局部变量,只要在用之前初始化即可
class Ooo{
final int a = 5; 使用之前初始化
final int b;
Ooo(){
b = 8;
}
}
static final常量
static final修饰的成员变量称为常量,
必须声明同时初始化,不可被改变。
通过类名.来访问
建议:常量名所有的字母都大写
static final常量在编译时自动替换为具体的值----效率高
(static final常量会在编译时被替换成常量值,所以在运行期间,Foo类不需要载入。)
没有类的加载过程
public static final double PI = 3.14159;
抽象方法
1. 由abstract修饰
2. 只有方法的定义,没有方法的实现(无大括号)
抽象类
1. 由abstract修饰
2. 包含抽象方法的类必须是抽象类
3. 抽象类不能被实例化
4. 抽象类是需要被继承的,强制子类重写
对于继承的子类:
重写所有抽象方法,也声明为抽象类
5. 抽象类的意义
包含公共的属性和行为,被子类所共享
为所有子类提供一种统一的公共类型--向上造型
包含抽象方法,为子类提供一个统一的入口,子类有不同的实现方法
abstract class Shape{ 抽象类--不完整
double c;
abstract void area(); 抽象方法--不完整
}
public class Demo1 {
public static void main(String[] args) {
Shape[] s = new Shape[4]; Shape类虽然不能实例对象,但是可以实例一个数组
s[0] = new Squre(2.0); 向上造型
s[1] = new Squre(4.0);
s[2] = new Circle(3.0);
s[3] = new Circle(4.0);
double max = s[0].area();
for(int i=0; i<4; i++) {
System.out.println(s[i].area());
if(s[i].area()>max) {
max = s[i].area();
}
}
System.out.println("the big number is "+max);
}
}
abstract class Shape{
public double c;
abstract double area();
}
class Squre extends Shape{
Squre(double c){
this.c = c;
}
double area() {
return c*c;
}
}
class Circle extends Shape{
Circle(double c){
this.c = c;
}
double area() {
return Math.PI*c*c/2;
}
}
打印:
4.0
16.0
14.137166941154069
25.132741228718345
the big number is 25.132741228718345
[接口]中的数据默认都是常量,方法默认的都是抽象的
1. 是一个标准、规范
2. 由interface定义
3. 只能包含常量和抽象方法
4. 接口不能被实例化
5. 接口是需要被实现/继承的,实现类/子类,必须重写接口中的所有抽象方法
6. 一个类可以实现多个接口,用逗号分隔
若又继承又实现时,应先继承extends后实现implements
7. 接口可以继承接口
package day10;
public class Demo2 {}
interface Inter1{
public static final double PI = 3.1415926;
public abstract void show(); 抽象方法
int NUM = 250; 默认都是public static final
void say(); 默认都是public abstract
}
interface Inter2{
void c();
}
class Aoo implements Inter1,Inter2{
public void show() { } 实现接口的抽象方法必须是public的
public void say() { }
public void c() { }
}
所有子类都一样的方法提取到父类
部分子类相同的方法提取到接口
多态:
1. 意义
--同一类型的引用指向不同的对象时,有不同的实现 --- 行为的多态
--同一对象被造型为不同的类型时,有不同的方法 --- 对象的多态
2. 向上造型
--父类型的引用指向子类的对象
--能造型成的类型有:父类型、实现的接口
--能点出来什么,看引用的类型
3. 强制类型转换,成功的条件有两个:
引用所指向的对象,就是该类型
引用所指向的对象,实现了该接口
Boo是Aoo的子类,Boo实现Inter1接口
Aoo o1 = new Boo(); 向上造型,正确
Boo o2 = (Boo) o1; 实现了该接口
Inter1 o3 = (Coo)o1; 错误,ClassCastException类型转换异常
4. 通过instanceof来判断引用是否是某种类型
instanceof返回boolean结果
强转成功的条件就是它为true的条件
内部类
1. 成员内部类:不太常用
--类中套类,外面的叫外部类,里面的叫内部类
--内部类通常只服务于外部类,对外不具备可见性
--内部类对象通常是在外部类中创建的
--内部类中可以直接访问外部类的成员(包括私有)
--内部类中有个隐式的引用指向创建它的外部类,语法---类名.this.
2. 匿名内部类:比较常用
--内部类有自己独立的.class
--如果想创建一个类的对象,并且对象只被创建一次
此时该类不必命名,称为匿名内部类
--匿名内部类中访问外部的数据,该数据必须是final的
class Aoo{ --外部类
private int a=5;
void show(){
Boo o = new BOo(); --编译正确
}
class Boo{ --内部类--Aoo的一个成员
void test(){ --可以调用Aoo中的私有变量
System.out.println(a); --相当于Aoo.this.a,而不是this.a(这个是指代Boo)
}
}
内部类在程序其他位置被调用,而不仅仅是在外部类中调用,只需要返回一个对象
public class Demo7 {
public static void main(String[] args) {
Too too = new Too();
too.createBoo().bb(); --调用createBoo()方法返回一个Boo对象,
--直接可以调用Boo中的方法
}
}
class Too{
private int a = 10;
public Boo createBoo() { --创建内部类Boo的对象
Boo boo = new Boo();
return boo;
}
class Boo{
void bb() { --访问外部类的私有变量
System.out.println("the private num is "+a);
System.out.println(Too.this.a); --上面中的a就相当于这个
}
}
}
打印结果:
the private num is 10
10
问:内部类有独立的.class吗?
答:有
package test;
public class test1 {
1.创建了Inter2的一个子类,没有名字
2.为该子类创建了一个对象,引用o2
3.大括号中的为子类的类体
public static void main(String[] args) {
int num = 8;
Inter8 o2 = new Inter8() {
public void show() {
System.out.println("no name show");
System.out.println(num); 如果不能打印出num,可以试试在num前加final
}
};
o2.show();
}
}
interface Inter8{
public void show();
}
打印结果:
no name show
8
凡是类,一编译,都会生成.class,
这个文件生成的类有
test1$1.class
test.class
Inter8.class
面向对象三大特征总结:
1. 封装:
--类:封装的是对象的属性和行为
--方法:封装的是具体的逻辑功能实现
--访问控制修饰符:封装的是访问的权限
2. 继承:
--作用:避免代码重复,有利于代码的重用
--父类:所有子类所共有的属性和行为
子类:子类所特有的属性和行为
--子继承(extends)父后,子具有:父+子
--传递性、单一继承、多接口实现
3. 多态:
--意义:行为的多态、对象的多态
--向上造型、强制类型转换、instanceof
--多态的表现形式:重写+重载
Eclipse Debug:找到错误的出处
F5:逐步调试(进入到方法中)
F6:逐过程调试(不会进入到方法中)
F7:跳出当前的方法
F8:跳出下一个断点,若没有断点了则直接结束调试
写代码要考虑扩展性
> 重定向,覆盖操作
>> 重定向,追加操作
type 输出文件内容
& 与操作,不短路
&& 要求是boolean,短路
<< 空位补0,被移除的高位丢弃,空缺位补0
>> 被移位的二进制最高位是0,右移后,空缺位补0;最高位是1,空缺位补1
>>> 被移位二进制最高位无论是0还是1,空缺位都用0补。
Java没有显式的指针操作,引用就是指针。