地址
Java简介 Java应用程序的运行机制:
Java是编译型和解释型的结合
流程:
Java首先利用文本编辑器编写Java源程序,源文件的后缀名为.java
;
再利用,编译器(javac)将源程序编译成字节码文件
,字节码文件的后缀名为.class
;
最后利用虚拟机(解释器,java)解释执行。
JVM(Java Virtual Machine)
就是一个虚拟的用于执行字节码文件的“虚拟计算机”
Java Runtime Environment(JRE)
包含:Java虚拟机
、库函数
、运行Java应用程序
所必须的文件。
Java Development Kit(JDK)
包含:包含JRE,以及增加编译器
和调试器
等用于程序开发的文件。
三者关系:
基础 1 2 3 4 5 public class hello1 { public static void main (String[] args) { System.out.println("hello world" ); } }
一个源文件中至多只能声明一个public的类
原文件名必须和其中定义的public的类名相同,且以“.java”为扩展名。
正确编译后的源文件,会得到相应的字节码文件,编译器为每个类生成独立的字节码文件
main方法是Java应用程序的入口方法
变量 1 2 3 4 5 6 7 8 9 10 11 12 public class hello1 { int a = 1 ; static b = 2 ; final num = 10 ; public static void main (String[] args) { { int c = 5 ; } System.out.println(a); } }
3类8种基本数据类型
数值型: byte,short,int,long,float,double
字符型: char
布尔型: boolean
1 2 3 4 int a = 15 ; int b = 015 ; int c = 0x15 ; int d = 0b1101 ;
整形的默认类型是int,所以如果要用long,就必须在后面添加L
浮点型的默认类型是double , 如果要用float,就必须在后面添加F
1 2 3 4 5 6 7 8 9 10 public class hello3 { public static void main (String[] args) { int a = 2000000 ; long b = 10000000000000L ; float c = 1.23F ; System.out.println(015 ); } }
浮点型是不精确的,所以不能比较。或者可以使用java.math包的类:BigInteger和BigDecima 进行比较。
字符串与字符 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class hello1 { public static void main (String[] args) { char a = 'a' ; char b = 'b' ; System.out.println(a+b); System.out.println("" +a+b); } } public class hello2 { public static void main (String[] args) { String a = "abc" ; String b = "def" ; System.out.println(a+b); } }
数据类型的转化 建议运算的数据类型要一致。
和python不一样,python跨类型运算时,只是转化结果的类型
X = 1.5+4
1.5和4都保持原先的float类型和int类型,其结果X升级为float类型
Java是数据自身转变
int X =byte 1 + int 2
byte 1 自己变成int 1
也就是说,没有long时,结果为int。即使操作数全为short,byte,结果也是int
数据类型的强制转化
1 2 3 4 目标数据类型变量 = (目标数据类型) (被转化的数据) eg : (int)b
1 2 3 4 5 6 7 8 public class hello2 { public static void main (String[] args ){ byte a = 3; int b = 4; byte c = (byte ) (a+b ); System .out .println (c ); /*7*/ } }
虽然在Java的变量运算
中数据类型升级和python不一样。但是Java常量运算
的数据类型升级和python是一样的:
变量运算,先提升类型再说。
常量运算,先算一下结果,再判断可不可以。
原因:变量只能在运行的时候确定他的值,常量在编译的时候就可以确定值了。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class hello3 { public static void main (String[] args) { byte a = 2 ; byte b = 1 ; byte c = (byte ) (a+b); byte d = 1 + 2 ; System.out.println(c); System.out.println(d); } }
为什么byte,short,char类型在运算的时候会自动转换成int?
本质上说,在定义byte,short类型时,他们接受的是一个int类型的数据,然后自己把它们截成了自己的类型。(如果数据超出了他们的范围,那么就会报错)
1 2 int b = a++ int b = ++a
各个类型的转换
强制转化 1 2 3 4 5 6 7 public class hello4 { public static void main (String[] args) { byte c = (byte ) 130 ; System.out.println(c); } }
注意 : 整形的默认类型是int
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class hello4 { public static void main (String[] args) { int money = 1000000000 ; int years = 20 ; int total1 = money * years; long total2 = money * years; long total3 = money * (long ) years; long total4 = (long ) money * years; System.out.println(total1); System.out.println(total2); System.out.println(total3); System.out.println(total4); } }
原地算法的强制转化类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class hello4 { public static void main (String[] args) { byte a = 1 ; a = a + 1 ; System.out.println(a); } } public class hello4 { public static void main (String[] args) { byte a = 1 ; a += 1 ; System.out.println(a); } }
原因:
扩展的赋值运算符其实隐含了一个强制类型转换 。
所以说,a +=1并不是等价于a = a + 1 而是等价于a =(a的数据类型) (a + 1)
Scanner 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.Scanner;public class hello4 { public static void main (String[] args) { Scanner scaner = new Scanner(System.in); System.out.println("your name?" ); String name = scaner.nextLine(); System.out.println("your age?" ); int age = scaner.nextInt(); System.out.println(name); System.out.println(age); } }
控制语句 if 1 2 3 4 5 6 7 8 9 10 11 12 13 public class hello1 { public static void main (String[] args) { char a = 'a' ; char b = 'b' ; if (a == 'a' ){ System.out.println("111" ); }else if (a == 'y' ){ System.out.println("222" ); }else { System.out.println("333" ); } } }
Switch
表达式限定byte,short,int,char,string。
Case后面只能接常量,不能是变量 。
break中断switch语句。不加break则会穿透执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class hello2 { public static void main (String[] args) { byte a = 3 ; switch (a){ case 3 : System.out.println("111" ); break ; case 4 : System.out.println("222" ); break ; default : System.out.println("333" ); } } }
for 1 2 3 4 5 6 7 public class hello3 { public static void main (String[] args) { for (int a=1 ; a<5 ; a++){ System.out.println(a); } } }
while 1 2 3 4 5 6 7 8 9 public class hello4 { public static void main (String[] args) { byte a = 0 ; while (a <5 ){ System.out.println(a); a++; } } }
Do 1 2 3 4 5 6 7 8 9 public class hello3 { public static void main (String[] args) { int a = 1 ; do { System.out.println(a); a++; }while (a != 3 ); } }
带标签的break和continue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class hello { public static void main (String[] args) { outer: for (int a = 10 ; a > 5 ; a--) { System.out.println("a : " + a); inner: for (int b = 0 ; b < a; b++) { System.out.println("b : " + b); if (b == 3 ) { break outer; } } } } }
面向对象和面向过程 Java里的has a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class student { String name; int age; int id; Computer computer; void play_game () { System.out.println("i play_game with " +computer.brand); } public static void main (String[] args) { student student = new student(); student.name = "hyl" ; student.age = 21 ; student.id = 311 ; Computer com = new Computer(); com.brand = "lenovo" ; com.money = 3500 ; student.computer = com; student.play_game(); } } class Computer { String brand; int money; void show_brand (String brand) { System.out.println(brand); } }
方法 1 2 3 4 修饰符 返回值类型 方法名(参数类型 参数名1. 参数类型 参数名2. ..){ 语句; return 返回值; }
eg:
1 2 3 4 5 6 7 8 9 10 11 12 public class hello4 { public static void main (String[] args) { int a = 10 ; int res = my_sum(a); System.out.println(res); } public static int my_sum (int a) { int c = a + 1 ; return c; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class hello5 { public static void main (String[] args) { hello5 hh = new hello5(); System.out.println(hh.test()); System.out.println(test()); } public static int test () { return 5 ; } }
递归调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class hello2 { public static void main (String[] args) { System.out.println(febo(5 )); } static int febo (int num) { if (num <= 2 ) { return num; } else { return febo(num - 1 ) + febo(num - 2 ); } } }
方法重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class hello5 { public static void main (String[] args) { int a = 1 ; int b = 2 ; int c = 4 ; System.out.println(sum(a, b)); System.out.println(sum(b, c)); System.out.println(sum(a, b, c)); } public static int sum (int a, int b) { return a + b; } public static float sum (float a, float b) { return a + b; } public static int sum (int a, int b, int c) { return a + b + c; } }
类的构造方法和构造方法的重载 1 Student hyl = new Student();
构造方法通过new关键字调用
构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值。
如果我们没有定义构造器,则编译器会自动定义一个无参的构造函数。如果已定义则编译器不会自动添加!
构造器的方法名必须和类名一致!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class student { String name; int age; int id; public student (String name,int age,int id) { this .name = name; this .age = age; this .id = id; } public student (String name,int age) { this .name = name; this .age = age; } public static void main (String[] args) { student studnet1 = new student("hyl" ,21 ,311 ); student studnet2 = new student("dsz" ,55 ); System.out.println(studnet1.name); System.out.println(studnet2.age); } }
Java的构造方法里调用其他构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class student3 { String name; int age; int id; public student3 (String name,int age) { this .name = name; this .age = age; } public student3 (String name,int age,int id) { this (name,age); this .id = id; } }
创建一个对象分为如下四步:
分配对象空间,并将对象成员变量初始化为0或空
执行属性值的显式初始化
执行构造方法
返回对象的地址给相关的变量
静态修饰符static 用static声明的成员变量为静态成员变量
,也称为类变量
。
简单来说 : static修饰的成员变量和方法从属于类 , 而不是从属于对象 。普通变量和方法从属于对象的。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享 。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
类变量的生命周期和类相同,在整个应用程序执行期间都有效。
static是静态修饰符,什么叫静态修饰符呢?
在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间 ,也就是只要程序在运行,那么这块内存就会一直存在。
这样做有什么意义呢?在Java程序里面,所有的东西都是对象,而对象的抽象就是类,对于一个类而言,如果要使用他的成员,那么普通情况下必须先实例化对象后,通过对象的引用才能够访问这些成员,但是用static修饰的成员可以通过类名加.
进行直接访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Student { String name; int age; static String school = "gdgydx" ; public Student (String name,int age) { this .name = name; this .age = age; } public void print_name () { System.out.println(name); } public static void print_school () { System.out.println(school); } public static void main (String[] args) { Student hyl = new Student("hyl" ,21 ); hyl.print_school(); hyl.school = "new school" ; hyl.print_school(); } }
总结:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,静态变量存储在静态区
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。 实例变量存储在堆
static语句块 在类加载的时候static语句块就会执行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Student { static String school = "gdgydx" ; static { int a = 4 ; System.out.println("AAA" ); System.out.println("BBB" ); System.out.println(a); print_school(); } String name; int age; static void print_school () { System.out.println(school); } public static void main (String[] args) { System.out.println("--- start ---" ); } }
如果类有继承关系,那么static语句块的执行顺序就是从父类往下走 。构造方法的执行顺序也是从父类往下走 。这种方案称为继承树的追溯
参数传值机制 Java的参数传值机制都是值传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Student { static String school = "gdgydx" ; String name; int age; public Student (String name, int age) { this .name = name; this .age = age; } void change_age (Student s) { s.age = 55 ; } public static void main (String[] args) { Student hyl = new Student("hyl" , 21 ); System.out.println(hyl.age); hyl.change_age(hyl); System.out.println(hyl.age); } }
Java虚拟机的内存管理 分成三个区域:
栈 Stack
:存放局部变量
栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧 (存储局部变量、操作数、方法出口等)
JVM 每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
栈属于线程私有 ,不能实现线程间的共享!
栈是由系统自动分配,速度快。栈是一个连续的内存空间。
堆 heap
:存放所有类的实例
堆用于存储创建好的对象和数组 (数组也是对象)
JVM只有一个堆,被所有线程共享
堆是一个不连续的内存空间,分配灵活,速度慢!
方法区 method area
:也叫静态区,类似于 C 中的静态区+代码区
,存放永远不变或唯一的内容。
方法区用来存放程序中永远不变或唯一的内容。 (类信息[Class对象]、静态变量、字符串常量等)
JVM只有一个方法区,被所有线程共享。
方法区实际也是堆 ,只是用于存储类、常量相关的信息。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class student { String name; int age; int id; Computer computer; void play_game () { System.out.println("i play_game with " + computer.brand); } public static void main (String[] args) { student student = new student(); student.name = "hyl" ; student.age = 21 ; student.id = 311 ; Computer com = new Computer(); com.brand = "lenovo" ; com.money = 3500 ; student.computer = com; student.play_game(); } } class Computer { String brand; int money; void show_brand (String brand) { System.out.println(brand); } }
简单来说,一旦要执行某个方法
创建一个栈帧,如果这个方法再调用其他方法,则会再创建一个栈帧
一旦栈帧里的变量需要用到,就会去堆和静态区里找
继承
Java只有单继承
子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法 )但不见得可以直接访问(比如,父类私有的属性和方法)。
定义一个类时,没有调用extendse的父类是:java.lang.Object
。
判断hyl是不是Person类的实例对象:System.out.println(hyl instanceof Person);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { int age; String name; public void rest (Student s) { System.out.println(s.name + " is resting now" ); } } class Student extends Person { int stu_id; } class TestExtends { public static void main (String[] args) { Student hyl = new Student(); hyl.name = "hyl" ; hyl.age = 21 ; hyl.stu_id = 5 ; hyl.rest(hyl); } }
方法重写 方法的重写需要符合下面的三个要点:
==
:方法名,形参列表相同
<=
:返回值类型
和声明异常类型
,子类小于等于父类
>=
:访问权限,子类大于等于父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Person { int age; String name; public void rest () { System.out.println("Person resting now" ); } public Person who_is () { return new Person(); } } class Student extends Person { int stu_id; public void rest () { System.out.println("Student resting now" ); } public Student who_is () { return new Student(); } } class TestExtends { public static void main (String[] args) { Student hyl = new Student(); hyl.rest(); System.out.println(hyl.who_is()); } }
Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。
Object类的方法 toString() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Student { String name; int age; static String school = "gdgydx" ; public Student (String name,int age) { this .name = name; this .age = age; } public String toString () { return name + age; } public static void main (String[] args) { Student hyl = new Student("hyl" ,21 ); System.out.println(hyl.toString()); } }
equals() ==
比较的是地址
。equals源码 :
1 2 3 public boolean equals (Object obj) { return (this == obj) }
也就是说,Object里的equals
也是比较地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Student { String name; int age; public Student (String name,int age) { this .name = name; this .age = age; } public static void main (String[] args) { Student s1 = new Student("hyl" ,21 ); Student s2 = new Student("hyl" ,21 ); System.out.println(s1==s2); System.out.println(s1.equals(s2)); } }
但是我们可以重写equals
, 使其不是比较地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Student { int id; String name; public Student (int id , String name) { this .id = id; this .name = name; } public boolean equals (Object obj) { if (this == obj) return true ; if (obj == null ) return false ; if (getClass() != obj.getClass()) return false ; Student s = (Student) obj; if (id != s.id) return false ; return true ; } public static void main (String[] args) { Student s1 = new Student(1 ,"hyl" ); Student s2 = new Student(1 ,"hyl" ); System.out.println(s1==s2); System.out.println(s1.equals(s2)); } }
super() super是直接父类对象的引用 。可以通过super来访问父类中被子类覆盖的方法或属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class TestExtends { public static void main (String[] args) { Student hyl = new Student(); new Student().func(); } } class Person { public int age; public void func () { age = 100 ; System.out.println("Person.age is " + age); } } class Student extends Person { public int age; public void func () { super .func(); age = 200 ; System.out.println("Student.age is " + age); System.out.println(age); System.out.println(super .age); } }
构造器方法的调用顺序 构造方法第一句总是:super()
,调用父类对应的构造方法。但是一般省略不写。
所以流程就是:先向上追溯Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person { public Person () { System.out.println("here is Person __new__" ); } } class Student extends Person { public Student () { System.out.println("here is Student __new__" ); } } class TestExtends { public static void main (String[] args) { System.out.println("--- start ---" ); new Student(); } }
封装 访问控制符 Java 中的访问控制权限可以分为 4 级
修饰符
类内部
同一个包
子类
任何地方
private
Yes
default
Yes
Yes
protected
Yes
Yes
Yes
public
Yes
Yes
Yes
Yes
1 2 3 4 5 6 7 8 9 10 11 class hellojava { public static void main (String[] args) { Student hyl = new Student(); System.out.println(hyl.age); } } class Student { private int age; }
类属性的处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class TestExtends { public static void main (String[] args) { Student hyl = new Student(); System.out.println(hyl.getAge()); hyl.setAge(21 ); System.out.println(hyl.getAge()); } } class Person { private int age; public void setAge (int age) { this .age = age; } public int getAge () { return this .age; } }
这种方式被称为JavaBean
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Person { private String name; private int age; public Person () { } public Person (String name, int age) { this .name = name; setAge(age); } public void setName (String name) { this .name = name; } public String getName () { return name; } public void setAge (int age) { if (age > 130 || age < 0 ) { this .age = 18 ; } else { this .age = age; } } public int getAge () { return age; } @Override public String toString () { return "Person [name=" + name + ", age=" + age + "]" ; } } public class Test2 { public static void main (String[] args) { Person p1 = new Person(); p1.setName("小红" ); p1.setAge(-45 ); System.out.println(p1); Person p2 = new Person("小白" , 300 ); System.out.println(p2); } }
多态 多态指的是同一个方法调用,由于对象不同可能会有不同的行为。 现实生活中,同一个方法,具体实现会完全不同
多态的要点:
多态是方法的多态,不是属性的多态(多态与属性无关)
多态的存在要有3个必要条件:
父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Animal { public void shout () { System.out.println("叫了一声!" ); } } class Dog extends Animal { public void shout () { System.out.println("旺旺旺!" ); } public void seeDoor () { System.out.println("看门中...." ); } } class Cat extends Animal { public void shout () { System.out.println("喵喵喵喵!" ); } } public class TestPolym { public static void main (String[] args) { Animal a1 = new Cat(); animalCry(a1); Animal a2 = new Dog(); animalCry(a2); Dog dog = (Dog)a2; dog.seeDoor(); } static void animalCry (Animal a) { a.shout(); } }
结果为:
转型 父类引用指向子类对象,我们称这个过程为向上转型
,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型
。
1 2 3 Animal a2 = new Dog();
1 2 3 4 5 6 7 8 9 10 11 public class TestCasting { public static void main (String[] args) { Object obj = new String("hyl" ); String str = (String) obj; System.out.println(str.charAt(0 )); System.out.println(obj == str); } }
编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。不然通不过编译器的检查 。
向上可以自动转型:
向下需要强制类型转换:
1 2 Animal a2 = new Dog(); Dog dog = (Dog)a2;
final关键字
修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
修饰方法:该方法不可被子类重写 。但是可以被重载
修饰类: 修饰的类不能被继承 。比如:Math、String等。
1 2 3 final int MAX_SPEED = 120 ;final void study () {}final class A {}
抽象方法和抽象类
抽象方法
:使用 abstract 修饰的方法,没有方法体,只有声明 。定义的是一种规范
,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类
:包含抽象方法的类就是抽象类。通过 abstract 方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 abstract class Student { abstract public void study () ; public void run () { System.out.println("i am runing" ); } } class HYL extends Student { public void study () { System.out.println("i am studying" ); } } public class TestExtends { public static void main (String[] args) { HYL hyl = new HYL(); hyl.study(); hyl.run(); } }
抽象类的使用要点:
有抽象方法的类只能定义成抽象类。
抽象类不能实例化,即不能用new来实例化抽象类 。
抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
抽象类只能用来被继承。
抽象方法必须被子类实现。
接口 为什么需要接口?接口和抽象类的区别?
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束 。
全面地专业地实现了:规范和具体实现的分离
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类员有的公共方法规范。
接口就是规范,定义的是一组规则,体现了现实世界中如果你是……,则必须能……
的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人。如果你是坏人,则必须欺负好人。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
定义接口的详细说明
访问修饰符:只能是public或默认。
接口名:和类名采用相同命名机制。
extends:接口可以多继承。
常量:接口中的属性只能是常量 ,总是:public static final 修饰。不写也是。
方法:接口中的方法只能是:public abstract 。 省略的话,也是public abstract。
要点:
子类通过implements来实现接口中的规范。
接口不能创建实例 ,但是可用于声明引用变量类型。
一个类实现了接口,必须实现接口中所有 的方法,并且这些方法只能是public的。
接口中只能包含静态常量、抽象方法,静态方法,不能有普通属性、构造方法、普通方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface MyInterface1 { public static final int MAX_AGE = 100 ; void func1 () ; } public interface MyInterface2 { void func2 () ; } class MyClass implements MyInterface1 ,MyInterface1 { public void func () { System.out.println(MAX_AGE); System.out.println("..." ); } }
内部类 把一个类放在另一个类的内部定义 ,称为内部类(innerclasses)。内部类可以使用public、default、protected 、private以及static修饰。而外部顶级类只能使用public
和default
修饰。
内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。
对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。
所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。
内部类的作用:
内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问 。
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
内部类的分类 内部类主要分为
成员内部类(非静态内部类、静态内部类)、
匿名内部类、
局部内部类。
非静态内部类
、静态内部类
的区别就是class
和static class
成员内部类 非静态内部类 外部类里使用非静态内部类和平时使用其他类没什么不同
非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
非静态内部类不能有静态方法、静态属性和静态初始化块。
外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class TestExtends { public static void main (String[] args) { Outer.Inner inner = new Outer().new Inner () ; inner.show(); } } class Outer { private int age = 10 ; class Inner { int age = 20 ; public void show () { int age = 30 ; System.out.println(age); System.out.println(this .age); System.out.println(Outer.this .age); } } }
非静态内部类有点类似于python中的闭包:
1 2 3 4 5 6 7 8 def func (): a = 1 def inner (): print (a) return inner() func() inner()
内部函数能获取外部函数的变量
内部函数只能由外部函数来调用
静态内部类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestExtends { public static void main (String[] args) { Outer.Inner inner = new Outer.Inner(); inner.show(); } } class Outer { private int age = 10 ; static class Inner { int age = 20 ; public void show () { int age = 30 ; System.out.println(age); System.out.println(this .age); } } }
使用要点:
当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。
静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:静态内部类.名字
的方式访问静态内部类的静态成员,通过 new 静态内部类()访问静态内部类的实例。
匿名内部类 适合那种只需要使用一次的类。比如:键盘监听操作等等。
匿名内部类没有访问修饰符。
匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestExtends { public static void main (String[] args) { TestExtends.function(new Outer() { public void func () { System.out.println("new.Outer.func()" ); } }); } public static void function (Outer out) { out.func(); } } class Outer { public void func () { System.out.println("Outer.func()" ); } }
局部内部类 类定义在方法内部的,作用域只限于本方法,称为局部内部类
。
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test2 { public static void main (String[] args) { new Test2().show(); } public void show () { class Inner { public void fun () { System.out.println("helloworld" ); } } new Inner().fun(); } }
数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class student3 { public static void main (String[] args) { int [] arr = new int [5 ]; for (int i = 0 ; i < arr.length; i++) { arr[i] = i * 10 ; } for (int i = 0 ; i < arr.length; i++) { System.out.println(arr[i]); } } }
数组的初始化 数组的初始化方式总共有三种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class student3 { public static void main (String[] args) { int [] arr1 = {1 ,2 ,3 ,4 ,5 }; int [] arr2 = new int [3 ]; int [] arr3 = new int [2 ]; arr3[0 ] = 1 ; arr3[1 ] = 2 ; } }
foreach循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class student3 { public static void main (String[] args) { int [] arr1 = {1 ,2 ,3 ,4 ,5 }; for (int i=0 ; i <arr1.length; i++){ System.out.println(arr1[i]); } for (int each : arr1){ System.out.println(each); } } }
数组的拷贝,删除,插入,扩容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Student { public static void main (String[] args) { String[] s1 = { "aa" , "bb" , "cc" , "dd" , "ee" }; String[] s2 = new String[10 ]; System.arraycopy(s1, 2 , s2, 6 , 3 ); for (int i = 0 ; i < s2.length; i++) { System.out.println(s2[i]); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Student { public static void main (String[] args) { String[] s1 = {"aa" ,"bb" ,"cc" ,"dd" ,"ee" }; System.arraycopy(s1,3 ,s1,2 ,s1.length-3 ); s1[s1.length-1 ] = null ; for (int i=0 ; i<s1.length ;i++){ System.out.println(s1[i]); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Student { public static void main (String[] args) { String[] s1 = {"aa" ,"bb" ,"cc" ,"dd" ,"ee" }; String[] s2 = new String[s1.length+2 ]; System.arraycopy(s1,0 ,s2,0 ,s1.length); for (int i=0 ; i<s2.length ;i++){ System.out.println(s2[i]); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Student { public static void main (String[] args) { String[] s1 = {"aa" ,"bb" ,"cc" ,"dd" ,"ee" }; String[] s2 = new String[s1.length+1 ]; System.arraycopy(s1,0 ,s2,0 ,2 ); s2[2 ] = "XX" ; System.arraycopy(s1,2 ,s2,3 ,3 ); for (int i=0 ; i<s2.length ;i++){ System.out.println(s2[i]); } } }
多维数组 1 2 3 4 5 6 7 8 9 10 11 12 import java.util.Arrays;public class Student { public static void main (String[] args) { int [][] a = new int [3 ][]; a[0 ] = new int []{1 ,2 ,3 }; a[1 ] = new int []{4 ,5 ,6 }; a[2 ] = new int []{7 ,8 ,9 }; System.out.println(Arrays.toString(a)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.util.Arrays;public class Student { public static void main (String[] args) { Object[] a1 = {1001 ,"高淇" ,18 ,"讲师" ,"2006-2-14" }; Object[] a2 = {1002 ,"高小七" ,19 ,"助教" ,"2007-10-10" }; Object[] a3 = {1003 ,"高小琴" ,20 ,"班主任" ,"2008-5-5" }; Object[][] emps = new Object[3 ][]; emps[0 ] = a1; emps[1 ] = a2; emps[2 ] = a3; System.out.println(Arrays.toString(emps[0 ])); System.out.println(Arrays.toString(emps[1 ])); System.out.println(Arrays.toString(emps[2 ])); } }
String类
Java字符串就是Unicode字符序列,例如字符串“Java”就是4个Unicode字符’J’、’a’、’v’、’a’组成的。
Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。
String str1 = "hyl";
和String str3 = new String("hyl");
是不一样的.
1 2 3 4 5 6 7 8 9 10 11 12 public class student4 { public static void main (String[] args) { String str1 = "hyl" ; String str2 = "hyl" ; String str3 = new String("hyl" ); System.out.println(str1 == str2); System.out.println(str1 == str3); } }
String类的常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class StringTest1 { public static void main (String[] args) { String s1 = "core Java" ; String s2 = "Core Java" ; System.out.println(s1.charAt(3 )); System.out.println(s2.length()); System.out.println(s1.equals(s2)); System.out.println(s1.equalsIgnoreCase(s2)); System.out.println(s1.indexOf("Java" )); System.out.println(s1.indexOf("apple" )); String s = s1.replace(' ' , '&' ); System.out.println("result is :" + s); } } public class StringTest2 { public static void main (String[] args) { String s = "" ; String s1 = "How are you?" ; System.out.println(s1.startsWith("How" )); System.out.println(s1.endsWith("you" )); s = s1.substring(4 ); System.out.println(s); s = s1.substring(4 , 7 ); System.out.println(s); s = s1.toLowerCase(); System.out.println(s); s = s1.toUpperCase(); System.out.println(s); String s2 = " How old are you!! " ; s = s2.trim(); System.out.println(s); System.out.println(s2); } }
String类源码分析 String类的部分源码 :
1 2 3 4 5 6 7 8 public final class String implements java .io .Serializable , Comparable <string >, CharSequence { private final char value[]; private final int offset; private final int count; }
在遇到字符串常量
之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的拼接。因此,在使用==进行String对象之间的比较时,我们需要特别注意,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TestString2 { public static void main (String[] args) { String str1 = "hello" + " java" ; String str2 = "hello java" ; System.out.println(str1 == str2); String str3 = "hello" ; String str4 = " java" ; String str5 = str3 + str4; System.out.println(str2 == str5); } }
StringBuffer和StringBuilder StringBuffer和StringBuilder非常类似,均代表可变的字符序列
。 这两个类都是抽象类AbstractStringBuilder的子类,方法几乎一模一样。
StringBuffer:线程安全,做线程同步检查, 效率较低。
StringBuilder:线程不安全,不做线程同步检查,因此效率较高。 建议采用该类。
AbstractStringBuilder 部分源码
1 2 3 4 5 abstract class AbstractStringBuilder implements Appendable , CharSequence { char value[]; ... }
1 2 3 4 5 6 7 8 9 10 public class student4 { public static void main (String[] args) { String a; StringBuilder sb = new StringBuilder("abcdef" ); System.out.println(sb); sb.setCharAt(2 ,'9' ); System.out.println(sb); } }
StringBuilder常用方法列表
重载的public StringBuilder append(…)
方法
可以为该StringBuilder 对象添加字符序列,仍然返回自身对象。
方法 public StringBuilder delete(int start,int end)
可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象。
方法 public StringBuilder deleteCharAt(int index)
移除此序列指定位置上的 char,仍然返回自身对象。
重载的public StringBuilder insert(…)
方法
可以为该StringBuilder 对象在指定位置插入字符序列,仍然返回自身对象。
方法 public StringBuilder reverse()
用于将字符序列逆序,仍然返回自身对象。
方法 public String toString()
返回此序列中数据的字符串表示形式。
和 String 类含义类似的方法:
1 2 3 4 5 6 public int indexOf (String str) public int indexOf (String str,int fromIndex) public String substring (int start) public String substring (int start,int end) public int length () char charAt (int index)
可变对象与不可变对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.Arrays;public class Test { public static void main (String[] args) { System.out.println(func1()); System.out.println(func2()); } public static String func1 () { String str = "" ; for (int i = 0 ; i < 5000 ; i++) { str = str + i; } return str; } public static String func2 () { StringBuilder str = new StringBuilder(); for (int i = 0 ; i < 5000 ; i++){ str.append(i); } return str.toString(); } }
str = str + i;
每次都会生成新对象,超级慢。
str.append(i);
并不会生成新对象。只是修改了原对象而已.
多次修改的情况下要使用str.append(i);
Array类
java.util.Arrays类,包含了常用的数组
操作,方便我们日常开发。
Arrays类包含了:排序、查找、填充、打印内容等常见的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.Arrays;public class student3 { public static void main (String[] args) { int [] a = { 1 ,2 ,323 ,23 ,543 ,12 ,59 }; System.out.println(a); System.out.println(Arrays.toString(a)); Arrays.sort(a); System.out.println(Arrays.toString(a)); System.out.println("index : " + Arrays.binarySearch(a, 12 )); } }
冒泡排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.Arrays;public class Student { public static void main (String[] args) { int [] arr = { 7 , 4 , 1 , 2 , 5 , 8 , 9 , 6 , 3 }; bubble_sort(arr); System.out.println(Arrays.toString(arr)); } public static void bubble_sort (int [] arr) { int temp; for (int i = arr.length - 1 ; i >= 0 ; i--) { for (int j = 0 ; j <= i; j++) { if (arr[i] < arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } }
二分查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Student { public static void main (String[] args) { int [] arr = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }; System.out.println(bin_search(arr, 5 )); } public static int bin_search (int [] arr, int target) { int left = 0 ; int right = arr.length; int mid; while (left < right) { mid = left + ((right - left) / 2 ); if (arr[mid] < target) { left = mid + 1 ; } else { if (arr[mid] > target) { right = mid; } else { return mid; } } } return -1 ; } }