1 任意类型与字符串的加法运算得到的都是字符串
2 可以使用标号,想跳出哪个循环就跳出哪个循环
break 和 continue 默认是跳出一层循环,但加了标号后可以跳出指定的循环
w:for(int x=1;x<10;x++)
{
q:for(int y=1;y<=x;y++)
{
System.out.print(x+"*"+y+"="+x*y+'\t');
break w;
}
System.out.println();
}
w:for(int x=1;x<10;x++)
{
q:for(int y=1;y<=x;y++)
{
System.out.print(x+"*"+y+"="+x*y+'\t');
continue w;
}
System.out.println();
}
3 重载:需要函数名称相同,通过参数列表不同来区分同名函数.
class Demo
{
public static int add(int a,int b)
{
return a+b;
}
public static void add(int a,int b,int c)
{
System.out.println(add(add(a,b),c));
}
public static void main(String[] args)
{
System.out.println(add(1,2));
add(1,2,3);
}
}
4 数组类型: 引用三种类型中的一种
int[] x = new int[3];
int[] y = {1,3,5,7,9}; //相当于int[] y = new int[]{1,3,5,7,9};
int[] z = x; z[0] = 100;
x = null;
x.length 数组的长度 (数组属性)
int []w = new int[3]; 自动初始化为0 ,boolean型是false
5 移位
算术右移
num = num >>> 1; (以0填充,单纯地移位,负数时会变成奇怪的数字)
逻辑右移
num = num >> 1; (以符号位填充,移动1位除以2,并且保留符号)
要注意的地方是 num >> 4 不是语句 要num = num >> 4; 这才能改变num的值
6 二维数组
1 动态初始化
//先分配高维的大小
int[][] arr = new int[3][];
arr[0] = new int[100];
arr[1] = new int[1];
arr[2] = new int[2];
//再分配每个的值
arr[0][0] = 1
System.out.println(arr.length); //3 二维数组长度
System.out.println(arr[0].length); //100 一维数组长度
2 静态初始化
int[][] arr1 = {{1,2,3},{4,5}};
3 遍历
for(int i=0;i<arr1.length;++i)
{
for(int j=0;j<arr1[i].length;++j)
{
System.out.print(arr1[i][j]+" ");
}
System.out.println();
}
4 int []x,y[]; x是一维,y是二维.
二维数组的表示方式有 int[][] y; int[] y[]; int y[][];
注意c语言中 int *x,y; x是指针 y是整形.
7 对象
class Car
{
String color = "红色";
int num = 4;
void run()
{
System.out.println(color+" "+num);
}
}
Car c = new Car();
成员变量在堆内存中
局部变量在栈内存中
8 匿名对象
new Car().run();
当对对象的方法只调用一次时 可以用匿名对象来调用.
9 构造代码块
构造代码块是给所有对象进行统一初始化 而构造函数是给对应的对象初始化
执行顺序是 : 构造代码块先于构造函数
class Car
{
private String color;
private int num;
Car(String _color,int _num)
{
color = _color;
num = _num;
System.out.println("Car()");
}
//不同对象的共性
{
System.out.println("block");
}
public void run()
{
System.out.println(color+" "+num);
}
}
10 构造函数调用构造函数只能用this
class Person
{
private int age;
String name;
Person(String name)
{
this.name = name;
}
Person(String name,int age)
{
this(name); //调用Person(String name)函数 并且只能放在构造函数第一句
this.age = age;
}
}
11 静态 statcic
当成员被静态修饰后,除了可以被对象调用外,还可以直接被类名调用.
static成员变量是共享的.存在数据区(/共享区/方法区).和函数存放在一起.
特点:
1,随着类的加载而加载.
2 优先于对象存在
3 被所有对象的所共享.
4 可以被类名调用.
静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态
静态方法中不可以使用this super关键字
主函数的定义:
public: 代表该函数访问权限已经是最大的
static: 代表主函数随着类的加载已存在
void: 主函数没有具体的返回值
main: 不是关键字 但是一个特殊被jvm识别的单词
String[] arr 函数的参数 参数是一个字符串数组
主函数是固定格式的 jvm识别
12 若要使用已编译好的类
虚拟机会去classpath中去找
set classpath=.;c:\myclasss
或者去环境变量中设置永久的
13 javadoc 可以生成自己的帮助文档
14 默认(不写时自动生成的)构造函数的权限是随着类权限的变化而变化的.
15 静态代码块
类加载时执行先静态代码块
class Demo
{
static
{
System.out.println("Demo start");
}
public static void main(String[] args)
{
System.out.println("haha");
}
static
{
System.out.println("Demo over");
}
}
结果是
F:\>java Demo
Demo start
Demo over
haha
先加载完类 (加载过程中执行静态代码块) 再执行其他语句
类在被使用时 才会加载
16 类的构造顺序
Person p = new Person("zhangsan",20);
这句话说明了什么事情:
1 因为new用到了Person类 所以会找到Person.class文件加载到内存中
2 执行该类中的static代码块
3 在堆中开辟空间 分配内存地址
4 在堆中建立对象特有属性 进行默认初始化
5 对属性进行显示初始化
6 对对象进行"构造代码块"初始化
7 对对象进行"构造函数"初始化
8 将内存地址赋给栈中的p变量
17 单例设计模式
设计一个类只能产生一个对象. 最方便 使用
class Demo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
s1.num = 30;
System.out.println(s2.num);
}
}
class Single
{
public int num;
private Single(){}
private static Single s = new Single();
public static Single getInstance()
{
return s;
}
}
-------------------------
懒汉式 (使用时创建对象) 会有同步问题
class Single
{
public int num;
private Single(){}
private static Single s;
public static Single getInstance()
{
if(s == null)
{
synchronized(Single.class) //注意这里要同步锁 以免同时几个函数访问导致多次建立对象
{
if(s==null)
{
s=new Single();
}
}
}
return s;
}
}
记住一个原则:定义单例时建议时候饿汉式.
18
Java中的访问权限控制符有四个.
作用域_____当前类____同一package___子孙类____其他package
public______√___________√__________√___________√
protected___√___________√__________√___________×
friendly_____√___________√__________×___________×
private_____√___________×__________×___________×
19 extends 继承
20 当子类中出现非私有的同名成员变量时,
子类要访问本类变量,使用this.
访问父类变量时,使用super.(父类对象的引用)
21 覆盖 (重写)
当子类继承父类,沿袭父类功能到子类中,但子类虽具备该功能,但功能的内容却和父类不一致,
这时没有必要定义新功能,而是使用覆盖特性,保留父类功能
1 子类覆盖父类 子类的权限必须大于等于父类的
2 静态只能覆盖静态
记住:
重载:只看同名函数的参数列表
重写:子父类方法包含返回值一模一样
不允许的情况:
父子类中的函数函数名参数一模一样,返回值不同.这不属于上面两种清空.
22 父子类中的构造函数
子类的所有构造函数,默认会访问父类中空参数的构造函数.
因为子类每个构造函数的第一行都有一句隐式super();
当父类没有空参数构造函数时,子类必须通过手动super形式来访问指定的构造函数
当然子类的构造韩式第一行也可以手动指定this语句来访问本类中的构造函数
class Demo
{
public static void main(String[] args)
{
Zi z = new Zi(5);
z.show();
}
}
class Fu
{
Fu(int num)
{
this.num = num;
}
int num;
}
class Zi extends Fu
{
Zi(int num)
{
super(num);
}
void show()
{
System.out.println(num); //num 即 super.num 继承来的 可以直接访问
}
}
23 final关键字 "最终"
1 可以修饰类 函数 变量
2 被final修饰的类不可以被继承
3 被final修饰的方法不能重写 (但可以重载,参数列表不同)
4 被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量又可以局部变量
而成员如果是个常量的话(反正也不变),可以加个public static final修饰 变成全局常量
5 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量.
24 抽象类 abstract
抽象方法一定在抽象类中
抽象方法和抽象类都必须被abstract修饰
抽象类不可以被new创建对象,因为调用抽象方法没有意义
抽象类中的方法要被使用,必须由子类复写所有的方法后,建立子类对象
如果子类只复写了部分抽象方法,那么该子类仍然是一个抽象类
25 接口
当抽象类中的方法都是抽象的,那么该类可以通过接口来表示
interface
常量和方法有固定修饰符 可以省略
常量 pblic static final
方法 public abstract
类与接口之间是实现关系 implements
class Demo
{
public static void main(String[] args)
{
zhangsan zs = new zhangsan();
lisi ls = new lisi();
zs.smoke();
zs.study();
ls.smoke();
ls.study();
ls.drink();
}
}
abstract class Student
{
public abstract void study();
}
interface intSmoke
{
public abstract void smoke();
}
interface intDrink
{
public abstract void drink();
}
class zhangsan extends Student implements intSmoke
{
public void study(){}
public void smoke(){}
}
class lisi extends Student implements intSmoke,intDrink
{
public void study(){}
public void smoke(){}
public void drink(){}
}
继承用来写共性,接口用来做扩展
26 多态
父类对象指向子类对象
提高程序扩展性
必须类与类中间有继承或者实现关系,存在覆盖
局限性: 只能使用父类的引用访问父类成员
如果想要调用猫的特有方法时 ,如何操作?
强制将父类的引用转成子类类型. 向下转型.(只有父类对象指向子类对象才可以)
instanceof 判断所属类型 一般在子类的数量有限时使用
class Demo
{
public static void main(String[] args)
{
MainBoard m = new MainBoard();
NetCard c = new NetCard();
SoundCard s = new SoundCard();
m.run();
m.usePCI(c);
m.usePCI(s);
m.closePCI(c);
m.closePCI(s);
}
}
class MainBoard
{
public void run()
{
System.out.println("mainboard run");
}
public void usePCI(PCI p)
{
if(p != null)
{
p.open();
}
}
public void closePCI(PCI p)
{
if(p != null)
{
p.close();
}
}
}
interface PCI
{
public abstract void open();
public abstract void close();
}
class NetCard implements PCI
{
public void open()
{
System.out.println("netcard open");
}
public void close()
{
System.out.println("netcard close");
}
}
class SoundCard implements PCI
{
public void open()
{
System.out.println("soundcard open");
}
public void close()
{
System.out.println("soundcard close");
}
}
27 (非静态)成员函数在多态调用时,编译看左边,运行看右边 //只有非静态成员函数有重写特性
编译期间看引用型变量所属的类中是否有调用的方法
运行期间看对象所属的类中是否有调用的方法
成员变量 编译运行都看左边
父类子类出现重名变量时,都是看引用型变量的.
28 Object 是所有对象的直接后者间接类
equals方法 Object 中的equals方法比较的是地址值 java认为所有对象都可以比较
class Demo
{
public static void main(String[] args)
{
Person a = new Person();
Person b = new Person();
Dog d = new Dog();
Person c = a;
System.out.println(a.equals(b)); //false
System.out.println(a.equals(d)); //false
System.out.println(a.equals(c)); //true
}
}
class Person
{
}
class Dog
{
}
重写equals
class Demo
{
public static void main(String[] args)
{
Person a = new Person(5);
Person b = new Person(5);
Object c = new Person(4);
Dog d = new Dog();
System.out.println(a.equals(b)); //true
System.out.println(a.equals(c)); //false
System.out.println(a.equals(d));//false
System.out.println(c.toString());
}
}
class Person
{
private int age;
Person(int age){this.age = age;}
public boolean equals(Object o)
{
if(o instanceof Person)
{
return age == ((Person)o).age;
}
else return false;
}
}
class Dog
{
}
29 toString() Object中的方法 打印出类名@@哈希值
一般也需要重写
30 内部类
内部类可以访问类中的私有变量,之所以可以直接访问外部类中的成员是因为内部类中持有外部类的引用 外部类名.this
内部类可以被private修饰
外部类要访问内部类,必须建立内部对象
class Demo
{
public static void main(String[] args)
{
Outer o = new Outer();
o.method() ;
//直接访问内部对象
Outer.Inner i = new Outer().new Inner();
i.function();
}
}
class Outer
{
int x = 3;
class Inner
{
//int x = 4;
void function()
{
//int x = 5;
System.out.println("inner:"+x); // x 前面省略了 Outer.this. 优先级由大到小是局部变量,本类变量,外部类变量
}
}
void method()
{
Inner in = new Inner();
in.function();
}
}
31 当内部类被static修饰时,只能访问外部类中的static成员.
在外部其他类中访问static内部类的非静态成员
new Outer.Inner().function();
在外部其他类中访问static内部类的静态成员
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是静态的
当外部类中的静态方法访问内部类时,内部类也必须是静态的
32 内部类定义在局部时:
1 不可以被成员修饰符修饰 (public private 等)
2 可以直接访问外部类中成员
但不可以访问它所在局部中的变量,只能访问fianl修饰的变量
33 异常
try
{
//代码块
}
catch (Exception e)
{
//处理异常
}
finally
{
//无论如何都会执行到的地方 通常用来释放资源
}
//---------------------------
class Demo
{
public static void main(String[] args)
{
try
{
System.out.println(Test.div(1,-1));
}
catch(Exception e)
{
e.printStackTrace(); // 异常名称:异常信息 异常问出现的位置 .(推荐)
//System.exit(0); //finally 只有一种情况下不会执行 就是当执行到这句时
}
finally
{
System.out.println("finally");
}
System.out.println("over");
}
}
class Test
{
//在功能上通过throws声明该功能会抛出的异常
//如果里面有自定义异常,但不用throws声明会报错
//对于有抛出异常的功能 如果不进行捕获或者抛出 会报错(和C++不一样)
static public int div(int a,int b) throws FuShuException,Exception
{
if(b<0) throw new FuShuException("负数");
return a/b;
}
}
class FuShuException extends Exception
{
//子类在构造时 通过super将异常信息给父类
FuShuException(String msg)
{
super(msg);
}
}
自定义异常必须是继承Exception,异常类和异常对象都需要被抛出,它们都具备可抛性.
这个可抛性是throwable体系中的独有特点.
只有这个体系中的类和对象才可以被throws 和 throw操作
34 throw 和 throws的区别
1 throws使用在函数上 throw使用在函数内
2 throws后面跟的异常类,可以跟多个
throw后面跟异常对象.
35比较特殊的异常
Exception有一个特殊的子类异常 RuntimeException
如果在函数内抛出该异常,函数上可以不声明throws一样通过
如果在函数上声明了该异常,调用者可以不进行处理,编译一样通过.
36 包 package
用来对文件进行分类管理
给类提供多层明明空i安
写在程序文件的第一行
类名的全称是包名.类名
包也是一种封装形式
package pack; //指定包
class Demo
{
public static void main(String[] args)
{
System.out.println("hello package");
}
}
编译时这么写 自动生成pack文件夹 -d是指定类文件存放的目录 并且生成包所需要的文件夹
F:\>javac -d . 123.java
执行
F:\>java pack.Demo //寻找当前目录下 或者 classpath目录下
hello package
比如我将我的类设置到c:\\myclass下
F:\>javac -d c:\myclass 123.java
F:\>java pack.Demo
错误: 找不到或无法加载主类 pack.Demo
F:\>set classpath=c:\myclass
F:\>java pack.Demo
hello package
37 多线程
创建现成第一种方式:继承Thread类
步骤:
1 定义类继承Thread
2 复写Thread中的run方法
目地:将自定义代码存储在run方法中,让线程运行
3 调用线程的start方法
该方法两个作用:启动线程,调用run方法
多线程的特性:随机性,谁抢到资源谁执行,至少执行多长,CPU说了算
package pack;
class Demo
{
public static void main(String[] args)
{
ThreadDemo td = new ThreadDemo();
td.start();
ThreadDemo td2 = new ThreadDemo();
td2.start();
for(int i=0;i<50;i++)
System.out.println("main run"+i);
}
}
class ThreadDemo extends Thread
{
public void run()
{
for(int i=0;i<50;i++)
System.out.println("thread run"+i);
}
}
38 dt.run() 与 dt.start()的区别:
ThreadDemo td = new ThreadDemo(); 创建一个线程.
dt.run() 调用线程中的方法,线程创建了没有执行 (单线程)
dt.start() 执行线程,并在线程中执行run中的方法 (多线程)
39 线程的名字
线程有默认名字 Thread-编号
设置线程名字:
通过构造函数中调用super能够设置线程名称
或者 setName()
获取线程名称
getName()
this.getName() //继承thread方法中可以使用
或者 Thread.currentThread().getName() //实现Runnable接口时 使用
通过名字可以辨别目前是哪个线程在执行.
---------------
package pack;
class Demo
{
public static void main(String[] args)
{
ThreadDemo td = new ThreadDemo();
td.start();
ThreadDemo td2 = new ThreadDemo();
td2.start();
for(int i=0;i<50;i++)
System.out.println("main run"+i);
}
}
class ThreadDemo extends Thread
{
ThreadDemo()
{
}
ThreadDemo(String name)
{
super(name); //线程名字
}
public void run()
{
for(int i=0;i<50;i++)
System.out.println(this.getName()+" "+i);
}
}
40 创建线程的第二种方式 -- 实现Runnable接口
1 定义类实现Runnable接口
2 覆盖Runnable中的run()方法
3 将Runnable接口的子类作为实际参数传给Thread的构造函数 建立线程对象
4 调用Thread类中的start方法启动线程接口中的run方法
实现方式和继承方式的区别:
1 实现方式好处: 避免单线程的局限性. (因此 实现Runnable接口是比较常用的方法)
2 继承方式:线程代码存放在Thread子类的run方法中
实现方式:线程代码存档在接口的子类的run方法中
3 资源共享:
使用继承方式时,一个Thread只能start一次,只有使用静态变量才能资源共享.
而使用实现方式时, 一个Runnable子类的对象实例化为多个Thread来start 以达到资源共享
//使用这种方法在卖票问题中不需要使用静态变量
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(tick > 0)
{
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
}
41 run方法中如果有异常不能使用throws抛出,只能try catch出来
(因为run方法是重写父类中的方法
在线程中Thread.sleep(100) 之后出现了安全问题 有了0 -1 -2的票
原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句
执行了一部分,还没有执行完,另一个线程参与进来执行,导致了共享
数据的错误.
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(true)
{
if(tick > 0)
{
try
{
Thread.sleep(100);
}
catch(Exception e){}
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
}
}
注意:
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
这样是没有意义的,这是各执行各的.
42
java对于多线程的安全问题提供了专业的解决方式: 同步代码块
synchronized(对象) //任意对象
{
需要被同步的代码
}
同步的前提:
1 两个或两个以上的线程
2 必须是多个线程使用同一个锁
必须保证同步中只能有一个线程的运行
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 500;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick > 0)
{
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
try
{
Thread.sleep(100);
}
catch(Exception e){}
}
}
}
}
首先,需要说明一点,也是最重要的一点,无论是同步方法 还是 同步块 都是只针对同一个对象的多线程而言的,只有同一个对象产生的多线程,才会考虑到 同步方法 或者是 同步块,如果定义多个实例的同步,可以考虑使用mutex,
对synchronized(this)的一些理解
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。(注意是执行完才能切换线程)
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用
相比于同步方法 同步代码块更好
如果能不用同步就不用同步,因为很慢
43 同步方法
在函数前面加上 synchronized
同步函数用的锁是this
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(true)
{
sold();
try
{
Thread.sleep(100);
}
catch(Exception e){}
}
}
synchronized private void sold()
{
if(tick > 0)
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
44 静态的同步方法,使用的锁是该方法所在字节码文件对象 类名.class
懒汉式在多线程是有安全隐患的,所以要设置成同步函数
class Single
{
private Single();
private Single s = null;
public synchronized static Single getInstance() //静态函数 它的同步锁是Single.class
{
if(null == s)
{
s = new Single();
}
return s;
}
}
//使用同步代码块 以及 双重判断 来较小同步带来的效率问题 双重判断
package pack;
class Demo
{
public static void main(String[] args)
{
T t = new T();
for(int i=0;i<20;i++)
{
new Thread(t).start();
}
System.out.println("last="+Single.getInstance().num);
}
}
class Single
{
private Single() {};
static private Single s = null;
public int num = 0;
public static Single getInstance()
{
if(null == s) //减小锁的影响
{
synchronized(Single.class) //仍然是在静态函数中 要用class来做锁
{
if(null == s)
{
s = new Single();
}
}
}
return s;
}
}
class T implements Runnable
{
private static int x = 100;
public void run()
{
Single s = Single.getInstance();
System.out.println(s.toString()); //可以显示实例的hash码
s.num = x++;
try
{
Thread.sleep(100);
}
catch(Exception e)
{
}
}
}
// 饿汉式就不用加同步
class Single
{
private Single();
private Single s = new Single();
public synchronized static Single getInstance() //静态函数 它的同步锁是Single.class
{
return s;
}
}
45 死锁
同步里面嵌套同步
package pack;
class Demo
{
public static void main(String[] args)
{
//这里有两个实例,这个例子跟锁有关,跟同步无关. 注意同步这里只创建一个
Test test1 = new Test(true);
Test test2 = new Test(false);
Thread t1 = new Thread(test1);
Thread t2 = new Thread(test2);
t1.start();
t2.start();
}
}
class Test implements Runnable
{
private boolean flag;
Test(boolean b)
{
flag = b;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
2 可以使用标号,想跳出哪个循环就跳出哪个循环
break 和 continue 默认是跳出一层循环,但加了标号后可以跳出指定的循环
w:for(int x=1;x<10;x++)
{
q:for(int y=1;y<=x;y++)
{
System.out.print(x+"*"+y+"="+x*y+'\t');
break w;
}
System.out.println();
}
w:for(int x=1;x<10;x++)
{
q:for(int y=1;y<=x;y++)
{
System.out.print(x+"*"+y+"="+x*y+'\t');
continue w;
}
System.out.println();
}
3 重载:需要函数名称相同,通过参数列表不同来区分同名函数.
class Demo
{
public static int add(int a,int b)
{
return a+b;
}
public static void add(int a,int b,int c)
{
System.out.println(add(add(a,b),c));
}
public static void main(String[] args)
{
System.out.println(add(1,2));
add(1,2,3);
}
}
4 数组类型: 引用三种类型中的一种
int[] x = new int[3];
int[] y = {1,3,5,7,9}; //相当于int[] y = new int[]{1,3,5,7,9};
int[] z = x; z[0] = 100;
x = null;
x.length 数组的长度 (数组属性)
int []w = new int[3]; 自动初始化为0 ,boolean型是false
5 移位
算术右移
num = num >>> 1; (以0填充,单纯地移位,负数时会变成奇怪的数字)
逻辑右移
num = num >> 1; (以符号位填充,移动1位除以2,并且保留符号)
要注意的地方是 num >> 4 不是语句 要num = num >> 4; 这才能改变num的值
6 二维数组
1 动态初始化
//先分配高维的大小
int[][] arr = new int[3][];
arr[0] = new int[100];
arr[1] = new int[1];
arr[2] = new int[2];
//再分配每个的值
arr[0][0] = 1
System.out.println(arr.length); //3 二维数组长度
System.out.println(arr[0].length); //100 一维数组长度
2 静态初始化
int[][] arr1 = {{1,2,3},{4,5}};
3 遍历
for(int i=0;i<arr1.length;++i)
{
for(int j=0;j<arr1[i].length;++j)
{
System.out.print(arr1[i][j]+" ");
}
System.out.println();
}
4 int []x,y[]; x是一维,y是二维.
二维数组的表示方式有 int[][] y; int[] y[]; int y[][];
注意c语言中 int *x,y; x是指针 y是整形.
7 对象
class Car
{
String color = "红色";
int num = 4;
void run()
{
System.out.println(color+" "+num);
}
}
Car c = new Car();
成员变量在堆内存中
局部变量在栈内存中
8 匿名对象
new Car().run();
当对对象的方法只调用一次时 可以用匿名对象来调用.
9 构造代码块
构造代码块是给所有对象进行统一初始化 而构造函数是给对应的对象初始化
执行顺序是 : 构造代码块先于构造函数
class Car
{
private String color;
private int num;
Car(String _color,int _num)
{
color = _color;
num = _num;
System.out.println("Car()");
}
//不同对象的共性
{
System.out.println("block");
}
public void run()
{
System.out.println(color+" "+num);
}
}
10 构造函数调用构造函数只能用this
class Person
{
private int age;
String name;
Person(String name)
{
this.name = name;
}
Person(String name,int age)
{
this(name); //调用Person(String name)函数 并且只能放在构造函数第一句
this.age = age;
}
}
11 静态 statcic
当成员被静态修饰后,除了可以被对象调用外,还可以直接被类名调用.
static成员变量是共享的.存在数据区(/共享区/方法区).和函数存放在一起.
特点:
1,随着类的加载而加载.
2 优先于对象存在
3 被所有对象的所共享.
4 可以被类名调用.
静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态
静态方法中不可以使用this super关键字
主函数的定义:
public: 代表该函数访问权限已经是最大的
static: 代表主函数随着类的加载已存在
void: 主函数没有具体的返回值
main: 不是关键字 但是一个特殊被jvm识别的单词
String[] arr 函数的参数 参数是一个字符串数组
主函数是固定格式的 jvm识别
12 若要使用已编译好的类
虚拟机会去classpath中去找
set classpath=.;c:\myclasss
或者去环境变量中设置永久的
13 javadoc 可以生成自己的帮助文档
14 默认(不写时自动生成的)构造函数的权限是随着类权限的变化而变化的.
15 静态代码块
类加载时执行先静态代码块
class Demo
{
static
{
System.out.println("Demo start");
}
public static void main(String[] args)
{
System.out.println("haha");
}
static
{
System.out.println("Demo over");
}
}
结果是
F:\>java Demo
Demo start
Demo over
haha
先加载完类 (加载过程中执行静态代码块) 再执行其他语句
类在被使用时 才会加载
16 类的构造顺序
Person p = new Person("zhangsan",20);
这句话说明了什么事情:
1 因为new用到了Person类 所以会找到Person.class文件加载到内存中
2 执行该类中的static代码块
3 在堆中开辟空间 分配内存地址
4 在堆中建立对象特有属性 进行默认初始化
5 对属性进行显示初始化
6 对对象进行"构造代码块"初始化
7 对对象进行"构造函数"初始化
8 将内存地址赋给栈中的p变量
17 单例设计模式
设计一个类只能产生一个对象. 最方便 使用
class Demo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
s1.num = 30;
System.out.println(s2.num);
}
}
class Single
{
public int num;
private Single(){}
private static Single s = new Single();
public static Single getInstance()
{
return s;
}
}
-------------------------
懒汉式 (使用时创建对象) 会有同步问题
class Single
{
public int num;
private Single(){}
private static Single s;
public static Single getInstance()
{
if(s == null)
{
synchronized(Single.class) //注意这里要同步锁 以免同时几个函数访问导致多次建立对象
{
if(s==null)
{
s=new Single();
}
}
}
return s;
}
}
记住一个原则:定义单例时建议时候饿汉式.
18
Java中的访问权限控制符有四个.
作用域_____当前类____同一package___子孙类____其他package
public______√___________√__________√___________√
protected___√___________√__________√___________×
friendly_____√___________√__________×___________×
private_____√___________×__________×___________×
19 extends 继承
20 当子类中出现非私有的同名成员变量时,
子类要访问本类变量,使用this.
访问父类变量时,使用super.(父类对象的引用)
21 覆盖 (重写)
当子类继承父类,沿袭父类功能到子类中,但子类虽具备该功能,但功能的内容却和父类不一致,
这时没有必要定义新功能,而是使用覆盖特性,保留父类功能
1 子类覆盖父类 子类的权限必须大于等于父类的
2 静态只能覆盖静态
记住:
重载:只看同名函数的参数列表
重写:子父类方法包含返回值一模一样
不允许的情况:
父子类中的函数函数名参数一模一样,返回值不同.这不属于上面两种清空.
22 父子类中的构造函数
子类的所有构造函数,默认会访问父类中空参数的构造函数.
因为子类每个构造函数的第一行都有一句隐式super();
当父类没有空参数构造函数时,子类必须通过手动super形式来访问指定的构造函数
当然子类的构造韩式第一行也可以手动指定this语句来访问本类中的构造函数
class Demo
{
public static void main(String[] args)
{
Zi z = new Zi(5);
z.show();
}
}
class Fu
{
Fu(int num)
{
this.num = num;
}
int num;
}
class Zi extends Fu
{
Zi(int num)
{
super(num);
}
void show()
{
System.out.println(num); //num 即 super.num 继承来的 可以直接访问
}
}
23 final关键字 "最终"
1 可以修饰类 函数 变量
2 被final修饰的类不可以被继承
3 被final修饰的方法不能重写 (但可以重载,参数列表不同)
4 被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量又可以局部变量
而成员如果是个常量的话(反正也不变),可以加个public static final修饰 变成全局常量
5 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量.
24 抽象类 abstract
抽象方法一定在抽象类中
抽象方法和抽象类都必须被abstract修饰
抽象类不可以被new创建对象,因为调用抽象方法没有意义
抽象类中的方法要被使用,必须由子类复写所有的方法后,建立子类对象
如果子类只复写了部分抽象方法,那么该子类仍然是一个抽象类
25 接口
当抽象类中的方法都是抽象的,那么该类可以通过接口来表示
interface
常量和方法有固定修饰符 可以省略
常量 pblic static final
方法 public abstract
类与接口之间是实现关系 implements
class Demo
{
public static void main(String[] args)
{
zhangsan zs = new zhangsan();
lisi ls = new lisi();
zs.smoke();
zs.study();
ls.smoke();
ls.study();
ls.drink();
}
}
abstract class Student
{
public abstract void study();
}
interface intSmoke
{
public abstract void smoke();
}
interface intDrink
{
public abstract void drink();
}
class zhangsan extends Student implements intSmoke
{
public void study(){}
public void smoke(){}
}
class lisi extends Student implements intSmoke,intDrink
{
public void study(){}
public void smoke(){}
public void drink(){}
}
继承用来写共性,接口用来做扩展
26 多态
父类对象指向子类对象
提高程序扩展性
必须类与类中间有继承或者实现关系,存在覆盖
局限性: 只能使用父类的引用访问父类成员
如果想要调用猫的特有方法时 ,如何操作?
强制将父类的引用转成子类类型. 向下转型.(只有父类对象指向子类对象才可以)
instanceof 判断所属类型 一般在子类的数量有限时使用
class Demo
{
public static void main(String[] args)
{
MainBoard m = new MainBoard();
NetCard c = new NetCard();
SoundCard s = new SoundCard();
m.run();
m.usePCI(c);
m.usePCI(s);
m.closePCI(c);
m.closePCI(s);
}
}
class MainBoard
{
public void run()
{
System.out.println("mainboard run");
}
public void usePCI(PCI p)
{
if(p != null)
{
p.open();
}
}
public void closePCI(PCI p)
{
if(p != null)
{
p.close();
}
}
}
interface PCI
{
public abstract void open();
public abstract void close();
}
class NetCard implements PCI
{
public void open()
{
System.out.println("netcard open");
}
public void close()
{
System.out.println("netcard close");
}
}
class SoundCard implements PCI
{
public void open()
{
System.out.println("soundcard open");
}
public void close()
{
System.out.println("soundcard close");
}
}
27 (非静态)成员函数在多态调用时,编译看左边,运行看右边 //只有非静态成员函数有重写特性
编译期间看引用型变量所属的类中是否有调用的方法
运行期间看对象所属的类中是否有调用的方法
成员变量 编译运行都看左边
父类子类出现重名变量时,都是看引用型变量的.
28 Object 是所有对象的直接后者间接类
equals方法 Object 中的equals方法比较的是地址值 java认为所有对象都可以比较
class Demo
{
public static void main(String[] args)
{
Person a = new Person();
Person b = new Person();
Dog d = new Dog();
Person c = a;
System.out.println(a.equals(b)); //false
System.out.println(a.equals(d)); //false
System.out.println(a.equals(c)); //true
}
}
class Person
{
}
class Dog
{
}
重写equals
class Demo
{
public static void main(String[] args)
{
Person a = new Person(5);
Person b = new Person(5);
Object c = new Person(4);
Dog d = new Dog();
System.out.println(a.equals(b)); //true
System.out.println(a.equals(c)); //false
System.out.println(a.equals(d));//false
System.out.println(c.toString());
}
}
class Person
{
private int age;
Person(int age){this.age = age;}
public boolean equals(Object o)
{
if(o instanceof Person)
{
return age == ((Person)o).age;
}
else return false;
}
}
class Dog
{
}
29 toString() Object中的方法 打印出类名@@哈希值
一般也需要重写
30 内部类
内部类可以访问类中的私有变量,之所以可以直接访问外部类中的成员是因为内部类中持有外部类的引用 外部类名.this
内部类可以被private修饰
外部类要访问内部类,必须建立内部对象
class Demo
{
public static void main(String[] args)
{
Outer o = new Outer();
o.method() ;
//直接访问内部对象
Outer.Inner i = new Outer().new Inner();
i.function();
}
}
class Outer
{
int x = 3;
class Inner
{
//int x = 4;
void function()
{
//int x = 5;
System.out.println("inner:"+x); // x 前面省略了 Outer.this. 优先级由大到小是局部变量,本类变量,外部类变量
}
}
void method()
{
Inner in = new Inner();
in.function();
}
}
31 当内部类被static修饰时,只能访问外部类中的static成员.
在外部其他类中访问static内部类的非静态成员
new Outer.Inner().function();
在外部其他类中访问static内部类的静态成员
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是静态的
当外部类中的静态方法访问内部类时,内部类也必须是静态的
32 内部类定义在局部时:
1 不可以被成员修饰符修饰 (public private 等)
2 可以直接访问外部类中成员
但不可以访问它所在局部中的变量,只能访问fianl修饰的变量
33 异常
try
{
//代码块
}
catch (Exception e)
{
//处理异常
}
finally
{
//无论如何都会执行到的地方 通常用来释放资源
}
//---------------------------
class Demo
{
public static void main(String[] args)
{
try
{
System.out.println(Test.div(1,-1));
}
catch(Exception e)
{
e.printStackTrace(); // 异常名称:异常信息 异常问出现的位置 .(推荐)
//System.exit(0); //finally 只有一种情况下不会执行 就是当执行到这句时
}
finally
{
System.out.println("finally");
}
System.out.println("over");
}
}
class Test
{
//在功能上通过throws声明该功能会抛出的异常
//如果里面有自定义异常,但不用throws声明会报错
//对于有抛出异常的功能 如果不进行捕获或者抛出 会报错(和C++不一样)
static public int div(int a,int b) throws FuShuException,Exception
{
if(b<0) throw new FuShuException("负数");
return a/b;
}
}
class FuShuException extends Exception
{
//子类在构造时 通过super将异常信息给父类
FuShuException(String msg)
{
super(msg);
}
}
自定义异常必须是继承Exception,异常类和异常对象都需要被抛出,它们都具备可抛性.
这个可抛性是throwable体系中的独有特点.
只有这个体系中的类和对象才可以被throws 和 throw操作
34 throw 和 throws的区别
1 throws使用在函数上 throw使用在函数内
2 throws后面跟的异常类,可以跟多个
throw后面跟异常对象.
35比较特殊的异常
Exception有一个特殊的子类异常 RuntimeException
如果在函数内抛出该异常,函数上可以不声明throws一样通过
如果在函数上声明了该异常,调用者可以不进行处理,编译一样通过.
36 包 package
用来对文件进行分类管理
给类提供多层明明空i安
写在程序文件的第一行
类名的全称是包名.类名
包也是一种封装形式
package pack; //指定包
class Demo
{
public static void main(String[] args)
{
System.out.println("hello package");
}
}
编译时这么写 自动生成pack文件夹 -d是指定类文件存放的目录 并且生成包所需要的文件夹
F:\>javac -d . 123.java
执行
F:\>java pack.Demo //寻找当前目录下 或者 classpath目录下
hello package
比如我将我的类设置到c:\\myclass下
F:\>javac -d c:\myclass 123.java
F:\>java pack.Demo
错误: 找不到或无法加载主类 pack.Demo
F:\>set classpath=c:\myclass
F:\>java pack.Demo
hello package
37 多线程
创建现成第一种方式:继承Thread类
步骤:
1 定义类继承Thread
2 复写Thread中的run方法
目地:将自定义代码存储在run方法中,让线程运行
3 调用线程的start方法
该方法两个作用:启动线程,调用run方法
多线程的特性:随机性,谁抢到资源谁执行,至少执行多长,CPU说了算
package pack;
class Demo
{
public static void main(String[] args)
{
ThreadDemo td = new ThreadDemo();
td.start();
ThreadDemo td2 = new ThreadDemo();
td2.start();
for(int i=0;i<50;i++)
System.out.println("main run"+i);
}
}
class ThreadDemo extends Thread
{
public void run()
{
for(int i=0;i<50;i++)
System.out.println("thread run"+i);
}
}
38 dt.run() 与 dt.start()的区别:
ThreadDemo td = new ThreadDemo(); 创建一个线程.
dt.run() 调用线程中的方法,线程创建了没有执行 (单线程)
dt.start() 执行线程,并在线程中执行run中的方法 (多线程)
39 线程的名字
线程有默认名字 Thread-编号
设置线程名字:
通过构造函数中调用super能够设置线程名称
或者 setName()
获取线程名称
getName()
this.getName() //继承thread方法中可以使用
或者 Thread.currentThread().getName() //实现Runnable接口时 使用
通过名字可以辨别目前是哪个线程在执行.
---------------
package pack;
class Demo
{
public static void main(String[] args)
{
ThreadDemo td = new ThreadDemo();
td.start();
ThreadDemo td2 = new ThreadDemo();
td2.start();
for(int i=0;i<50;i++)
System.out.println("main run"+i);
}
}
class ThreadDemo extends Thread
{
ThreadDemo()
{
}
ThreadDemo(String name)
{
super(name); //线程名字
}
public void run()
{
for(int i=0;i<50;i++)
System.out.println(this.getName()+" "+i);
}
}
40 创建线程的第二种方式 -- 实现Runnable接口
1 定义类实现Runnable接口
2 覆盖Runnable中的run()方法
3 将Runnable接口的子类作为实际参数传给Thread的构造函数 建立线程对象
4 调用Thread类中的start方法启动线程接口中的run方法
实现方式和继承方式的区别:
1 实现方式好处: 避免单线程的局限性. (因此 实现Runnable接口是比较常用的方法)
2 继承方式:线程代码存放在Thread子类的run方法中
实现方式:线程代码存档在接口的子类的run方法中
3 资源共享:
使用继承方式时,一个Thread只能start一次,只有使用静态变量才能资源共享.
而使用实现方式时, 一个Runnable子类的对象实例化为多个Thread来start 以达到资源共享
//使用这种方法在卖票问题中不需要使用静态变量
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(tick > 0)
{
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
}
41 run方法中如果有异常不能使用throws抛出,只能try catch出来
(因为run方法是重写父类中的方法
在线程中Thread.sleep(100) 之后出现了安全问题 有了0 -1 -2的票
原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句
执行了一部分,还没有执行完,另一个线程参与进来执行,导致了共享
数据的错误.
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(true)
{
if(tick > 0)
{
try
{
Thread.sleep(100);
}
catch(Exception e){}
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
}
}
注意:
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
new Thread(new Ticket()).start();
这样是没有意义的,这是各执行各的.
42
java对于多线程的安全问题提供了专业的解决方式: 同步代码块
synchronized(对象) //任意对象
{
需要被同步的代码
}
同步的前提:
1 两个或两个以上的线程
2 必须是多个线程使用同一个锁
必须保证同步中只能有一个线程的运行
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 500;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick > 0)
{
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
try
{
Thread.sleep(100);
}
catch(Exception e){}
}
}
}
}
首先,需要说明一点,也是最重要的一点,无论是同步方法 还是 同步块 都是只针对同一个对象的多线程而言的,只有同一个对象产生的多线程,才会考虑到 同步方法 或者是 同步块,如果定义多个实例的同步,可以考虑使用mutex,
对synchronized(this)的一些理解
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。(注意是执行完才能切换线程)
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用
相比于同步方法 同步代码块更好
如果能不用同步就不用同步,因为很慢
43 同步方法
在函数前面加上 synchronized
同步函数用的锁是this
package pack;
class Demo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket implements Runnable
{
private int tick = 50;
public void run()
{
while(true)
{
sold();
try
{
Thread.sleep(100);
}
catch(Exception e){}
}
}
synchronized private void sold()
{
if(tick > 0)
System.out.println(Thread.currentThread().getName() + " " +tick--);
}
}
44 静态的同步方法,使用的锁是该方法所在字节码文件对象 类名.class
懒汉式在多线程是有安全隐患的,所以要设置成同步函数
class Single
{
private Single();
private Single s = null;
public synchronized static Single getInstance() //静态函数 它的同步锁是Single.class
{
if(null == s)
{
s = new Single();
}
return s;
}
}
//使用同步代码块 以及 双重判断 来较小同步带来的效率问题 双重判断
package pack;
class Demo
{
public static void main(String[] args)
{
T t = new T();
for(int i=0;i<20;i++)
{
new Thread(t).start();
}
System.out.println("last="+Single.getInstance().num);
}
}
class Single
{
private Single() {};
static private Single s = null;
public int num = 0;
public static Single getInstance()
{
if(null == s) //减小锁的影响
{
synchronized(Single.class) //仍然是在静态函数中 要用class来做锁
{
if(null == s)
{
s = new Single();
}
}
}
return s;
}
}
class T implements Runnable
{
private static int x = 100;
public void run()
{
Single s = Single.getInstance();
System.out.println(s.toString()); //可以显示实例的hash码
s.num = x++;
try
{
Thread.sleep(100);
}
catch(Exception e)
{
}
}
}
// 饿汉式就不用加同步
class Single
{
private Single();
private Single s = new Single();
public synchronized static Single getInstance() //静态函数 它的同步锁是Single.class
{
return s;
}
}
45 死锁
同步里面嵌套同步
package pack;
class Demo
{
public static void main(String[] args)
{
//这里有两个实例,这个例子跟锁有关,跟同步无关. 注意同步这里只创建一个
Test test1 = new Test(true);
Test test2 = new Test(false);
Thread t1 = new Thread(test1);
Thread t2 = new Thread(test2);
t1.start();
t2.start();
}
}
class Test implements Runnable
{
private boolean flag;
Test(boolean b)
{
flag = b;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}