环境
- java跨平台的原因是因为java是运行在虚拟机上的,针对不同的设备安装不同的虚拟机即可
- JVM:java虚拟机,真正运行java程序的地方
- 核心类库:java封装好的一些类
- 开发工具:包括javac(编译工具),java(运行工具),jdb(调试工具),jhat(内存分析工具)等等
- JDK:java开发工具包,由JVM+核心类库+开发工具组成
- JRE:java运行环境。由JVM+核心类库+运行工具。当别人把编译后的文件发送给其他人进行运行时,只需要安装JRE即可。
类型
- 字面量类型:整数,小数,字符串(双引号引起来),布尔,字符类型(单引号括起来,内容只能有一个,例如’A’)和空类型
- ‘\t’制表符:根据前面字符串的长度,如果不够8位,补齐到8位
- 基本数据类型:
- 整数: ,short,int,long(变量值后面加L,大小写都可以)
- 浮点数:float(变量值后面加F,大小写都可以),short
- 字符:chart
基础
项目结构:项目->module->包->类
类型转换
隐式转换(自动类型提升):小范围的类型自动提升为大范围的类型:byte < short < int < long < float < double.
强制类型转换:
格式:目标数据类型 变量名 = (目标数据类型)被强转的数据
例如
double a = 12.3;
int b = (int)a;
判断是否相等:== 判断是否不等:!= and运算符:& or运算符:| 短路and运算符:&& 短路or运算符:||
键盘输入:
Scanner sc = new Scanner(System.in);
System.out.println('请输入值');
int data = sc.nextInt()
生成0-99的随机数:
Random r = new Random()
int number = r.nextInt(100)for(;;):代表无限循环
while(true):代表无限循环
数组
定义:int [] array 或者 int array[]
静态初始化:int [] array = new int[]{1,2,3} 或者简写 int [] array = {1,2,3}
动态初始化:int [] array = new int[50],默认数据都是0
栈:存储方法 堆:只要new出来的就存储在堆中
public static void main(String[] args) {
int[] arr = new int[2]
}
main方法进栈
int[] arr进入栈中的main方法中
等号右边为new关键字,new出来的数组存储在堆中
int[] arr = 地址 该地址指向堆中存储的数据延伸一下,何为基本数据类型,何为引用数据类型
- 基本数据类型:数据存储在自己的空间当中,赋值给其他变量的是自己的真实的数据值。
- 引用数据类型:使用new关键字new出来的,数据值存储在其他空间中,自己的空间中存储的是指向其他空间的地址。引用数据类型赋值给其他变量的是地址
方法
定义和调用
public static void method() {
......
}
method()
带参数方法
public static void method(int num1 , int num2) {
......
}
method(10,20 )带返回值的方法
public static int method(int a , int b) {
int c = a + b
return c
}
//返回数组
public static int[] method() {
}方法的重载:同一个类中,方法名相同,参数不同的方法,就是重载。和返回值无关。
官方定义:多个同名的方法具有不同的参数类型和参数个数,这些同名的方法,就构成了重载关系
面向对象
定义类:一个文件定义一个class类
public class Phone() {
//成员变量
String brand;
double price;
//成员方法
public void call(){
system.out.println('')
}
public void playGame() {
........
}
}创建手机对象并使用
//创建对象
Phone p = new Phone();
//使用
p.brand = '小米'
.........用来描述一类事物的类不用写main方法,像上面的Phone类,这种类叫做javabean类
- 标准的javabean类
- 见名知意
- 成员变量使用private关键字,通过get ,set访问
- 提供至少两个构造方法
- 无参构造
- 带全部参数的构造
- 标准的javabean类
封装:告诉你如何正确的设计对象的属性和方法
针对private的成员变量,需要提供get和set方法
public class GirlFriend() {
private age;
public void setAge(a) {
if(a > 18) {
age = a
}
else {
system.out.println('不合法')
}
}
public void getAge() {
return age
}
}成员变量和局部变量重名
public class Test {
private int age; //0
public void method() {
int age = 18; //18
System.out.println(age); //age = 18. 就近原则
System.out.println(this.age) //age = 0 通过this可以访问到外部的成员变量
}
}构造方法:
- 名字必须和类名相同
- 每创建一次对象(实例化),构造函数都会执行一次
- 在没写构造方法时,虚拟机默认会添加一个空参构造方法
public class Student{
private string name;
private int age;
//空参构造
public Student() {
........
}
//带参构造
public student(String name , int age) {
this.name = name;
this.age = age
.........
}
get set方法.........
}
main函数中:
//创建对象
Student stu = new Student("zhangsan" , 18)
System.out.println(stu.getName());对象的内存图
- 堆内存只和new关键字有关,有new。那就是创建了一份堆空间
- 方法运行时所进入的内存为栈,变量也在这里
- 方法区中存储所有的字节码文件,也就是.class文件
创建对象的步骤:
Student s = new Student(); 1. 加载class文件 2. 声明局部变量 3. 在堆内存中开辟一份空间 4. 默认初始化 5. 显示初始化(如果JavaBean类在声明成员变量的时候赋值了,那么就需要初始化值) 6. 构造方法初始化 7. 将堆内存中的地址赋值给左边的局部变量
代码分析:
public class Student{
String name;
int age;
public void study() {
System.out.println('study');
}
}
public class TestStudent{
public static void main(String[] args) {
Student s = new Student();
s.name = "zs";
s.age = 12;
s.study()
}
}
程序执行:
先把TestStudent类加载到方法区,并临时存储main()
执行main函数,main方法被放入栈中
创建Student对象:
先把Student类加载到方法区中,并包含类的所有成员变量以及方法
Student s被存储到栈中的main方法中
在堆内存中开辟一个空间,把方法区中Student.class的成员变量复制一份,并且堆中还有成员方法的地址
Student s = 地址 , 该地址为堆内存中数据的地址图片解析:
两个对象的内存图:
this的原理
前文提到的this可以访问到外部成员变量的原理:this的本质是代表方法调用者的地址值
public class Student{
String name;
int age;
public void study() {
int age = 10
System.out.println(age);
System.out.println(this.age);
}
}
public class TestStudent{
public static void main(String[] args) {
Student s = new Student();
s.study()
}
}
this指向方法的调用者,也就是s,s在被创建时值为地址,指向的就是堆内存中的数据,所以通过this.name可以获取到成员变量的值
快捷键使用
- 快速生成main方法:psvm
- 快速生成构造方法:
- mac快速生成快捷键 command+n。 windows快捷键:alt+insert
- 安装插件ptg ,可以快速生成空参构造,全参构造,以及get set方法
字符串
- 在内存中,字符串存储在串池中,串池存储在堆内存中,所以字符串也是引用数据类型
- 当使用双引号直接对字符串变量赋值时系统会检查该字符串在串池中是否存在,如果不存在,就创建新的,如果存在,复用

每new一次就是开辟了一块新的 小空间,相同字符串不会复用 ,所以推荐使用直接赋值的方式,节约内存

string的字符串比较方法:equals(区分大小写),equalsIgnoreCase(不区分大小写)
StringBuilder:可以看作是一个容器,创建出来后里面的吗、内容是可以变化的
使用StringBuilder的场景:字符串的拼接,字符串的反转
//创建StringBuilder对象,StringBuilder既有无参构造(容器中不初始化值),也有有参构造
StringBuilder sb = new StringBuilder('abc')
//添加元素
sb.append(1)
sb.append(2.3)
sb.append(true) 输出为:abc 12.3true
//反转
sb.reverse()
//把StringBuilder类型变成字符串
String str = sb.toString()
//打印的是sb容器中的属性,不是该引用对象的地址值,是因为java底层做的处理
System.out.println(sb)StringJoiner:和StringBuilder差不多,是个容器,里面的内容也可以变化
//创建对象,StringJoiner只有有参构造,第一个参数(必选)为容器中字符串拼接的间隔符号,第二第三个参数(可选)为字符串的开始符号和结束符号
StringJoiner sj = new StringJoiner(",","[","]")
//添加元素
sj.add("aaa").add("bbb").add("ccc")
//输出为[aaa,bbb,ccc ]
System.out.println(sj)
//变成字符串
sj.toString()toString的底层方法就是new了一个字符串,带变量的字符串拼接(String a = b + “a”)底层就是先使用StringBuilder,在通过toString方法转换成字符串.所以得到的是个新地址值
集合ArrayList
集合:
长度是动态遍变化的,自动伸缩
存引用数据类型,如果要存基本数据类型,需要把他们变成对应的包装类
使用:<>代表泛型,用来限制数据类型
ArrayList<String> list = new ArrayList<>()
System.out,println(list) //[]ArrayList是java已经写好的一个类,这个类在底层做了处理,打印的不是地址值,是集合中存储的数据类型。在展示的时候数据会用[ ] 包起来。
ArrayList方法:
boolean add(E e) //添加元素,返回值表示是否添加成功
boolean remove(E e) //删除元素,返回值表示是否成功
E remove(int index) //删除指定索引的元素,返回值为被删除的元素
E set(int index , E e) //修改指定索引的元素,返回原来的元素
E get(int index) //获得指定索引的元素
int size() //集合中元素的个数集合中存放自定义类型的数据
定义了一个Student的javaBean类
public class Student() {
private String name;
private int age;
.........
get/set方法
空参构造和全参构造
}
使用
ArrayList<Student> list = new ArrayList<>()
Student s1 = new Student("张三", 18)
list.add(s1)
进阶
静态
static关键字,用来修饰成员变量或者成员方法,被static修饰的成员变量被所有实例对象共享
s //被static修饰的成员变量
public class Student{
.......其他私有成员变量.........
static String teacherName;
.......get/set方法以及成员方法
}
//在主函数中(主函数也叫测试类)
main(){
//该属性被所有Student类的实例对象共享
Student.teacherName = "张三"
}static修饰的方法一般写在工具类中,何为工具类?就是帮忙处理功能的类,比如:可以求最大值的工具类,求平均值的工具类,工具类中的构造方法是私有化的,目的为不让外界实例化他的对象,他里面的方法需要定义为static的,方便调用
public class AverageUtil(){
private AverageUtil() {};
public staic int Average(int[] arr ){
//里面写功能
}
}
- 静态方法中没有this关键字,普通函数的this指向调用它的对象,而static方法中没有this,所以会报错
- 静态方法中不能访问非静态的东西,也就是不能访问非静态方法和非静态变量,在如下的JavaBean类中,method方法为静态方法,其不能访问name和age两个非静态变量,主要原因还是因为其没有this,不知道是哪个对象的name和age。
package com.qianrui.staticUse; |
- 在内存方面,静态的东西随着着类的加载而加载,非静态的东西和对象有关,只要没创建对象,非静态的东西就不会被加载到内存中。
继承
- 使用extends继承
public class Student extends Person {} |
java只支持单继承,也就是一个儿子只能有一个父亲,不支持多继承,但是支持多重继承。
每一个类都直接或间接继承于Object类,默认继承。
子类只能访问父类非私有的成员
子类能继承父类中的哪些内容:
构造方法 非私有的不能继承,私有不能继承:因为如果可以继承父类的构造方法,那会导致构造方法和类名不相同。
成员变量 非私有能继承,私有能继承:虽然可以继承父类私有的成员变量,但是不能使用。
成员方法 非私有能继承,私有不能继承:java会在最顶层的父类设置一个虚方法表(非private方法,非static方法,非final方法),将自己的虚方法存放在虚方法表中,继承其的子类会继承其虚方法表,并在其中添加自己的虚方法,所以使用子类中没有的方法时,并不是一层一层往父类上找,而是先查询虚方法表,如果没有再一层一层往上找。
继承的内存图如下所示,在加载子类的字节码文件时,也会将父类的字节码文件加载进方法区
继承中成员变量,成员方法,构造方法的访问特点:
- 成员变量:就近原则,如果没有重名的成员变量,就直接通过名字访问,如果重名,则要访问本类的,使用this,访问父类的使用super。
public class father(){
String name = "father";
}
public class son extends father() {
String name = "son";
public void show(){
String name = 'temp';
System.out.println(name); //temp 就近原则
System.out.println(this.name); //son this访问本类成员变量
System.out.println(super.name); //father super代表父类的变量或方法
}
}成员方法:就近原则,和成员变量访问特点一样。当子类成员方法中的名字和父类成员方法中的名字相同,那就是重写,重写需要写@Override注解(注释是给程序员看的,注释是给虚拟机看得),校验子类重写时语法是否正确。
ublic class father(){
public void eat() {
System.out.println("eat")
}
}
public class son extends father() {
public void eat() {
System.oput.println("eat food")
}
}方法重写的原理:B类继承C类的虚方法表,重写method2方法,那么method2方法就属于B的,A继承的虚方法表中的method2为B的method2
重写方法的要求:
重写的方法名称,形参列表必须与父类中的一致
重写方法的访问权限和返回值类型尽量与父类保持一致
class Father {
public Dog eat() {
return null;
}
}
//重写方法时,子类的返回值类型大于父类,就会报错,Animal>Dog(范围)
class Son extends Father {
public Animals eat() {
return null;
}
}只有添加到虚方法表中的方法才能被重写
构造方法:子类中的构造方法默认先访问父类中的构造方法,再执行自己的,原因是子类在初始化的时候可能会用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据,所以子类在初始化之前先调用父类的构造方法来完成父类数据空间的初始化。因此子类构造方法的第一句就是super(),不写也存在,且必须在第一行,想调用父类的有参构造,必须手写super进行调用传参。
public class Person {
String name;
int age;
public Person() {
System.out.println("父类无参");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person {
public Student() {
//默认super()
super();
System.out.println("子类无参");
}
}
public class Test {
public static void main(String[] args) {
Student st = new Student(); //控制台会先输出父类无参,再输出子类无参
}
}public class Person {
String name;
int age;
public Person() {
System.out.println("父类无参");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
public Student() {
//默认super()
super();
System.out.println("子类无参");
}
public Student(String name , int age) {
super(name , age);
}
}
public class Test{
public static void main(String[] args) {
//想要创建学生对象的时候初始化值,就要在Student的有参构造中手动调用super传参
Student st = new Student('zs' , 18);
}
}
this(…)可以访问构造方法,代码如下所示,测试类中调用的无参构造,想要实现如果不赋值,默认名字为张三,则在Student的无参构造中调用有参构造,并赋默认值张三。
public class Student{
String name;
int age;
public Student() {
this("张三", null)
}
public Student(String name , int age) {
super(name , age);
}
}
public class Test {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.name); //张三
}
}
多态
多态的前提:(1)有继承关系(2)有父类引用子类对象,类似于Father f = new Son()(3)有方法重写
多态的好处:使用父类型作为参数,可以接受所有子类对象,提现多态的扩展性与便利
多态中调用成员方法,变量的特点:
- 调用成员变量:编译看左边,运行也看左边
- 调用成员方法:编译看左边,运行看右边
- 编译看左边导致多态不能调用子类的特有方法
public class Test{
public static void main(String[] args) {
//多态使用
Animal a = new Dog();
//编译看左边,javac编译代码时,会看左边的父类中有没有这个变量,如果有编译成功,如果没有编译失败
//运行看左边:多条使用语句Animal a = new Dog();中左边是Animal,所以输出为动物
System.out.println(a.name);//动物
//运行看右边:多条使用语句Animal a = new Dog();中左边是Dog,所以输出为狗方法
a.method();//狗方法
}
}
class Animal {
String name = "动物";
public void method() {
System.out.println("动物方法");
}
}
class Dog extends Animal {
String name = "狗";
public void method() {
System.out.println("狗方法");
}
}
class Cat extends Animal {
String name = "猫";
public void method() {
System.out.println("猫方法");
}
}
包
- 包就是文件夹,包名规则:公司域名+包的作用
- 使用同一包中的类不需要导包
- 使用java.lang包不需要导包
- 其他情况需要导包
- 如果同时使用两个包中的同名类,需要使用全类名(包名+类名)
final
用final修饰方法:该方法是最终方法,不能被重写
用final修饰类:该类是最终类,不能被继承
用final修饰变量:叫做常量,只能被赋值一次,final修饰的是基本数据类型,那么变量存储的数据值不能发生改变,如果修饰的变量是引用数据类型,那么存储的地址值不可以发生改变,对象内部可以发生改变。
final int[] ARR = {1,2,3,4,5};
ARR[0] = 4; //不会报错
ARR = new int[10]; //会报错
权限修饰符
抽象类
抽象类的作用:当一个父类的两个子类有共同的一个方法,但是方法内容不一样,此时可以在子类中进行方法的重写,但是如果子类的代码不是自己写,而是别人写,别人忘了重写,就会导致子类的该方法与实际不符,所以可以在父类中将该方法定义为抽象方法,这样子类就必须进行重写,不重写就会报错。包含抽象方法的类称为抽象类。
抽象方法:子类共同的方法名,但是方法体不一样,在父类中,这种方法可以称为抽象方法。
抽象方法定义格式:
public abstract 返回值类型 方法名(参数列表)
抽象类:一个类中存在抽象方法,则该类必须声明为抽象类。
抽象类定义格式
public abstract class 类名{}
抽象类注意事项:
抽象类不能实例化,也就是Person p = new Person();会报错
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类可以有构造方法,作用是当创建子类对象时,给子类对象赋值的,因为虽然父类不能实例化,但是子类如果不是抽象类,是可以实例化的,需要给子类的实例化对象进行赋值。
public abstract class Person {
String name;
int age;
public Person() {}
public Person(String name , int age) {
this.age = age;
this.name = name;
}
pubic abstract void work();
get/set...
}
public class Student extends Person {
public Student() {}
public Student(String name , int age) {
super(name , age)
}
public void work() {
System.out.println("Student")
}
}
public class Test{
public static void main(String[] args) {
Student s = new Student("zs",12);
System.out.println(s.getName() + '' + s.getAge());
}- 抽象类的子类:要么重写抽象类中的所有抽象方法,要么是抽象类
接口
- 比如父类为动物类,子类为兔子,狗和青蛙,狗和青蛙有一个公共属性游泳,但是这个属性不能写入父类中,因为不是公共的方法。但是如果各自写入子类当中,就不能规范书写的格式,所以需要定义一个接口来实现。
接口就是一种规则,是对行为的抽象。
接口的定义和使用:
接口使用interface来定义:public interface 接口名 {}
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示:public class 类名 implements 接口名 {}
类中需要重写接口中的所有抽象方法(类中也要重写父类中的所有抽象方法)
接口和类的实现关系可以单实现,也可以多实现:public 类名 implements 接口名1,接口名2 {}
当类中实现的多个接口中有重名的抽象方法,只需要重写一次即可。
实现类还可以再继承一个类的同时实现多个接口:public 类名 extends 父类 implements 接口名1,接口名2 {}
接口与接口也可以继承,可以单继承,也可以多继承(类不能多继承,只能多层继承)
代码示例见 ``package com.qianrui.interfaceTest` 包。
接口中成员的特点:
成员变量:只能是常量,默认修饰符:public static final
构造方法:没有构造方法
成员方法:只能是抽象方法,默认修饰符:public abstract
JDK7以前只能接口中只能定义抽象方法,JDK8之后接口中可以定义有方法体的方法,包括静态方法和默认方法,主要是为了解决接口升级的问题,默认方法格式如下,默认方法注意事项:
public default 返回值类型 方法名(参数列表) {} |
该方法不是抽象方法,不强制重写,如果被重写,重写时去掉default关键字
public可以省略,default不能省略,如果省略了default,默认是抽象方法,抽象方法不能有方法体
如果实现了多个接口,多个接口中存在相同名字的默认方法,实现类就必须对该方法进行重写
|
```
- 静态方法,静态方法不能重写
- JDK9以后新增了私有方法:主要是抽取默认方法或者静态方法中的一些公共代码,格式一是为默认方法服务的,格式二是为静态方法服务的
接口适配器:当一个接口中有多个方法,我们需要使用其中的一个方法,却需要重写所有抽象方法,不容易一眼看到想要的方法,所以设计接口适配器,在接口适配器中重写所有方法,然后使用实现类继承适配器,重写所需要的方法即可。
public interface Inter {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
public abstract void method5();
public abstract void method6();
}
//适配器写为抽象类是为了不让外界创建他的对象
public abstract class InterAdapter implement inter {
//重写method1-method5
}
public class InterImpl extends InterAdapter {
public void method5() {
}
}
内部类
在类的里面定义的类称为内部类,内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义
内部类的访问特点:(1)内部类可以直接访问外部类的成员,包括私有。(2)外部类要访问内部类的成员必须创建对象
包括:成员内部类,静态内部类(JDK16之后),局部内部类,匿名内部类
成员内部类
public class Car { 外部类 |
获取成员内部类的对象
方法一:
public class Car { 外部类
string carName;
int carAge;
int carcolor;
class Engine{ 成员内部类
string engineName;
int engineAge;
}
}
public class Test{
public static void main(String[] args) {
Car.Engine en = new Car().new Engine()
}方法二:外部类中编写方法,对外提供内部类对象。这种方法当内部类是私有的时候也可以用,上面的方法则不可以。
public class Car { 外部类
string carName;
int carAge;
int carcolor;
private class Engine{ 成员内部类
string engineName;
int engineAge;
}
public Engine getInstance() {
return new Engine();
}
}
public class Test{
public static void main(String[] args) {
Car c = new Car();
c.getInstance();
}静态内部类:只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
public class Car { 外部类 |
静态内部类
创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名()
public class Test{
public static void main(String[] args) {
Car.Engine en = new Car.Engine();
en.show();
en.show1();
}调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名()
局部内部类:定义在方法里面的类称为局部内部类,外界无法使用局部内部类,类似于局部变量,该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
public Outer{ 外部类 |
- 匿名内部类:隐藏了名字的内部类
格式: |
public interface Swim() { |
匿名内部类使用
public abstract class Animal {
public abstract void eat();
}
//测试文件
public class Test{
public static void main(String[] args) {
}
public static void method(Animal a) {
a.eat();
}
}在测试类中调用method方法,之前的方式:
public abstract class Animal {
public abstract void eat();
}
public class Dog extends Animal {
public void eat() {
sout("吃骨头")
}
}
//测试文件
public class Test{
public static void main(String[] args) {
Dog d = new Dog();
method(d);
}
public static void method(Animal a) {
a.eat();
}
}但是上面的书写方式有点麻烦,使用匿名内部类书写如下,不用再单独定义一个Dog类
public abstract class Animal {
public abstract void eat();
}
public class Test{
public static void main(String[] args) {
method(
new Animal() {
public void eat() {
sout("吃骨头")
}
}
);
}
public static void method(Animal a) {
a.eat();
}
}匿名内部类格式细节:包含了继承和实现,方法重写,创建对象
匿名内部类使用场景:当方法的参数是接口或者类时,如上例子所示