首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

Java并发编程实战系列5之基础构建模块

1 同步容器类 同步容器类包括Vector和HashTable,二者是早期JDK一部分,此外还包括在JDK 1.2中添加的一些功能相似的类,这些的同步封装器类是由Collections.synchronizedXxx等工厂方法创建的。这些类实现线程安全的方式是:将他们的状态封装起来,并对每个共有方法进行同步,使得每次只有一个线程能访问容器的状态。 1.1 同步容器类的问题 同步容器类都是线程安全的,但在某些情况可能需额外客户端加锁来保护复合操作。 容器上常见的复合操作包括: 迭代(反复访问元素,直到遍历完容器中所有元素) 跳转(根据指定顺序找到当前元素的下一个元素)以及条件运算 在同步容器类中,这些复合操作在没有客户端加锁的情况下,仍是线程安全的, 但当其他线程并发的修改容器时,他们可能会表现出意料之外的行为。 2 并发容器 Java5提供了多种并发容器来改进同步容器的性能。 同步容器将所有对容器状态的访问都串行化,以实现他们的线程安全性。 这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重降低。 并发容器是针对多个线程并发访问设计的。在Java 5中增加了 ConcurrentHashMap,用来替代同步且基于散列的Map,增加了对一些常见符合操作的支持,例如“若没有则添加”、替换以及有条件删除等。 CopyOnWriteArrayList,用于在遍历操作为主要操作的情况下代替同步的List。 copyOnWriteArrayList 和 copyOnWriteSet 一开始都共享同一个内容,当想要修改内容时,才会真正的把内容 copy 出去,形成一个新的内容后再改 比如:当我们往一个容器添加元素时,不直接往当前容器添加,而是先将容器进行 copy,复制出一个新容器,再往新容器里加元素。添加完之后,再将原容器引用指向新容器。 好处:对 copyOnWrite 容器进行并发读时,不需要加锁,因为当前容器不会增加新元素,读写分离 copyOnWriteArrayList#add要加锁,否则多线程时会 copy N 个副本 copyOnWrite 适合于 读多写少场景,但只能保证数据最终一致性,不保证实时一致性 若你希望写入马上被读到,不要用 copyOnWrite 容器 通过并发容器来代替同步容器,可以极大地提供伸缩性并降低风险。 2.1 CocurrentHashMap 同步容器在执行每个操作期间都持有一个锁。在一些操作中,例如HashMashMap.get或List.contains,可能包含大量的工作:当遍历散列桶或链表来查找某个特定的对象时,必须在许多元素上调用equals。在基于散列的容器中,如果hashCode不能很均匀的分布散列值,那么容器中的元素就不会均匀的分布在整个容器中。某些情况下,某个糟糕的散列函数还会把一个散列表变成线性链表。当遍历很长的链表并且在某些或者全部元素上调用equals方法时,会花费很长时间,而其他线程在这段时间内都不能访问容器。 ConcurrentHashMap使用一种粒度更细的称为分段锁的机制来实现更大程度的共享. 在这种机制中,任意数量的读取线程可以并发的访问Map,执行读操作的线程和执行写操作的线程可以并发的访问Map,并且一定数量的写线程可以并发的修改Map. ConcurrentHashMap与其他并发容器一起增强了同步容器:迭代器不会抛出ConcurrentModificationException,因此迭代过程无需加锁. 其迭代器具有"弱一致性",而并非"及时失败".可以容忍并发的修改,当创建迭代器时会遍历已有的元素,并可以(但不保证)在迭代器被构造后将修改操作反映给容器. 只有当需要加锁Map以进行独占访问时,才应该放弃使用ConcurrentHashMap. 2.2 额外的原子Map操作 由于ConcurrentHashMap不能被加锁来执行独占访问,因此 无法使用客户端加锁来创建新的原子操作. 一些常见的复合操作,eg."若没有则添加","若相等则移除"等,都已经实现为原子操作并且在ConcurrentMap接口中声明,如下面代码所示. public interface ConcurrentMap<K, V> extends Map<K, V> { //仅当K没有相应的映射值时才插入 V putIfAbsent(K key, V value); //仅当K被映射到V时才移除 boolean remove(Object key, Object value); //仅当K被映射到oldValue时才替换为newValue boolean replace(K key, V oldValue, V newValue); //仅当K被映射到某个值时才被替换为newValue V replace(K key, V value); }

优秀的个人博客,低调大师

JavaScript进阶【三】JavaScript面向对象的基础知识复习

版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79547464 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JavaScript面向对象知识复习</title> </head> <body> <h2></h2> <script type="text/javascript"> /************************************************类,成员属性,成员方法******************************************************/ /** * 定义一个类 * @param name * @param age * @constructor */ function MyClass(name, age) { this.name = name; this.age = age; // 成员方法 this.toString = function () { alert(cls1.name+":"+cls1.age); }; }; /** * 实例化一个cls1对象 * @type {MyClass} */ var cls1 = new MyClass("xiugang", 15); //alert(cls1.name+":"+cls1.age); cls1.toString(); // 再给这个类的一个对象cls2添加一个方法 var cls2 = new MyClass("zhangsan", 25); cls2.ShowName = function () { alert(this.name+":"+this.age); }; cls2.ShowName(); // 使用Prototype对象来给函数添加方法 function Animal(name, age) { this.name = name; this.age = age; } Animal.prototype.toString = function () { alert(this.name+":"+this.age); }; // 实例化两个对象 var dog = new Animal("dog", 15); dog.toString(); var cat = new Animal("cat", 16); cat.toString(); // 利用prototype属性给一个类添加多个方法 function Person(name, age) { this.name = name; this.age = age; }; Person.prototype = { toString : function () { alert(this.name+":"+this.age); }, sayHello : function () { alert("say Hello!"); } }; var student = new Person("小明", 25); student.sayHello(); student.toString(); /************************************************静态类******************************************************/ var StaticClass = function () { } StaticClass.name = "StaticClass"; StaticClass.Sum = function (value1, value2) { return value1 + value2; }; alert(StaticClass.name+", "+StaticClass.Sum(10, 20)); /************************************************继承******************************************************/ function PeopleClass() { this.type = "People"; }; PeopleClass.prototype = { getType : function () { alert("This is a Person"); } }; function StudentClass(name, sex) { // 使用apply方法将父类对象的构造函数绑定到子类对象上 PeopleClass.apply(this, arguments); this.name = name; this.sex = sex; } var stu = new StudentClass("小红", "女"); alert(stu.type); // 实现了属性的继承 /** * 实现方法的继承 */ function Sophermore(name, sex) { PeopleClass.apply(this, arguments); // 实现父类方法的继承 /** * 实现思路: 需要循环将父类对象的prototype进行赋值, 即可达到继承的目的 */ var prop; for (prop in PeopleClass.prototype){ var proto = this.constructor.prototype; if (!proto[prop]){ proto[prop] = PeopleClass.prototype[prop]; } proto[prop]["super"] = PeopleClass.prototype; } this.name = name; this.sex = sex; } var stu2 = new Sophermore("xiuxiu", 22); alert(stu2.type); stu2.getType() /** * 方法二:实现继承的第二种方法, 使用对象冒充的方法 */ function AnimalNew(name, age) { this.name = name; this.age = age; this.Sum = function () { alert(this.name+","+this.age); } } // 成员方法 /*AnimalNew.prototype = { sayhello : function () { alert(this.name+"is saying Hello!"); }, sayAge : function () { alert(this.name+"'s age is "+this.age); } }*/ AnimalNew.prototype.sayHello = function () { alert(this.name+" is saying Haha!"); } // 子类开始实现继承 function Duck(name, age) { this.animal = AnimalNew; this.animal(name, age); } var duck = new Duck("鸭子", 12); //duck.sayHello(); //error! //duck.sayAge(); //error! //duck.sayHello(); //error! duck.Sum(); //ok的! /************************************************JavaScript继承知识加强******************************************************/ function Animal(name) { // 属性 this.name = name; //实例方法 this.sleep = function () { console.log(this.name+"正在睡觉!"); } } // 原型方法 Animal.prototype.eat = function (food) { console.log(this.name+"正在吃"+food); } /** * 方法一: 将父类的实例作为子类的原型, 可以同时实现父类的属性和方法的继承 */ function Cat() { } Cat.prototype = new Animal(); Cat.prototype.name = "cat"; // test var cat =new Cat(); console.log(cat.name); cat.sleep() cat.eat("fish"); console.log(cat instanceof Animal); console.log(cat instanceof Cat); /** * 方法二: 组合继承 * 通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用 */ function Cow(name) { Animal.call(this); this.name = name; } Cow.prototype = new Animal(); Cow.prototype.constructor = Cat; var cow = new Cow("小牛博客"); console.log(cow.name); console.log(cow.sleep()); console.log(cat instanceof Animal); console.log(cat instanceof Cat); // 利用方法二:组合继承实现继承的综合案例 function Family(name, age) { // 属性 this.name = name; this.age = age; // 实例方法 this.Member = function () { alert("This family is having 5 memnbers now!"); } }; // 原型方法 Family.prototype = { sayHello : function () { alert(this.name +" is saying hello!"); }, sayAge : function () { alert(this.name +" is saying age:"+this.age); } }; // 开始实现继承 function Son(name, age) { Family.call(this); this.name = name; this.age = age; } Son.prototype = new Family(); Son.prototype.constructor = Family; // 开始测试 var son = new Son("王老大", 15); alert(son.age+", "+son.age); son.sayAge(); son.sayHello(); alert(son instanceof Family); alert(son instanceof Son); </script> </body> </html>

优秀的个人博客,低调大师

01.Java基础(多线程回顾,对比Linux多线程)

两个线程间通信,实现交替打印 public class Thread1 { public static void main(String[] args) { final Printer printer = new Printer(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print1(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print2(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } } /** * 等待唤醒机制,两个线程间通信 * @author renzhenming * */ class Printer{ private int flag = 1; public void print1() throws InterruptedException{ synchronized(this){ if (flag !=1) { /** * obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞, * 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1), * 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁, * 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。 * (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后, * T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法) * * 1.在同步代码块中,用哪个对象锁就用哪个对象调用wait方法, * 2.为什么wait和notify这些方法要定义在Object类中?因为锁对象可以是任意对象,那么任意对象的类都是Object的子类,Object是任意类的基类,所以将方法定义在Object这个类中就会让任意对象对其进行调用 * sleep和wait方法的区别:a.sleep在同步代码块或同步函数中,不释放锁,(睡着了也会抱着锁睡),wait相反会释放锁 b.sleep方法必须传入参数,参数就是时间,时间到了自动醒来,wait方法可以传入时间参数,也可以不传入时间参数,如果给wait方法传入时间参数,用法与sleep相似,时间到了就停止等待(通常都是用没有参数的wait方法) */ this.wait(); } System.out.print("y"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("z"); System.out.print("i"); System.out.print("\r\n"); flag = 2; /** * obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现), * 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁, * 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前, * 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。 */ this.notify(); } } public void print2() throws InterruptedException{ synchronized (this) { if (flag != 2) { wait(); } System.out.print("z"); System.out.print("h"); System.out.print("e"); System.out.print("n"); System.out.print("m"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("\r\n"); flag = 1; notify(); } } } 打印结果 yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming ... 三个线程或三个以上通信(试错,期待的结果是逐条打印,实际结果看下边) 按照上边的方式,代码如下: public class Thread1 { public static void main(String[] args) { final Printer printer = new Printer(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print1(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print2(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print3(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } } /** * 等待唤醒机制,两个线程间通信 * @author renzhenming * */ class Printer{ private int flag = 1; public void print1() throws InterruptedException{ synchronized(this){ if (flag !=1) { /** * obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞, * 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1), * 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁, * 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。 * (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后, * T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法) */ this.wait(); } System.out.print("y"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("z"); System.out.print("i"); System.out.print("\r\n"); flag = 2; /** * obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现), * 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁, * 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前, * 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。 */ this.notify(); } } public void print2() throws InterruptedException{ synchronized (this) { if (flag != 2) { wait(); } System.out.print("z"); System.out.print("h"); System.out.print("e"); System.out.print("n"); System.out.print("m"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("\r\n"); flag = 3; notify(); } } public void print3() throws InterruptedException{ synchronized (this) { if (flag != 3) { wait(); } System.out.print("f"); System.out.print("a"); System.out.print("l"); System.out.print("l"); System.out.print("i"); System.out.print("n"); System.out.print("l"); System.out.print("o"); System.out.print("v"); System.out.print("e"); System.out.print("\r\n"); flag = 1; notify(); } } } 结果: zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming yingzi zhenming fallinlove zhenming ... 为什么会出现上边的情况?我们极端分析一下,假设,第一个回合中,首先执行的是第2和第3个线程,也就是print2 print3方法,此时,由于flag不满足执行条件,两个线程都进入了等待状态,然后执行print1打印了一条,设置flag=2,然后notify,此时通常疏忽的开发人员可能会想当然的认为,flag=2 了,那么理所当然应该唤醒线程2了,应该打印print2了,事实上并非如此,由于notify是随机唤醒一个线程,那么它可能唤醒2 也可能唤醒3,假设此时被唤醒的线程是3,由于if语句的特性,不会重新判断执行条件,而是在哪里等待,在哪里醒来,于是直接跳过了判断语句执行了print3的打印内容,所以就出现了这种情况 针对这种问题,我们尝试用while代替if做条件判断,while是循环判断,每次判断都会判断标记,我们对程序做修改后如下: public class Thread1 { public static void main(String[] args) { final Printer printer = new Printer(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print1(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print2(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print3(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } } /** * 等待唤醒机制,两个线程间通信 * @author renzhenming * */ class Printer{ private int flag = 1; public void print1() throws InterruptedException{ synchronized(this){ while (flag !=1) { /** * obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞, * 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1), * 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁, * 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。 * (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后, * T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法) */ this.wait(); } System.out.print("y"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("z"); System.out.print("i"); System.out.print("\r\n"); flag = 2; /** * obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现), * 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁, * 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前, * 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。 */ this.notify(); } } public void print2() throws InterruptedException{ synchronized (this) { while (flag != 2) { wait(); } System.out.print("z"); System.out.print("h"); System.out.print("e"); System.out.print("n"); System.out.print("m"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("\r\n"); flag = 3; notify(); } } public void print3() throws InterruptedException{ synchronized (this) { while (flag != 3) { wait(); } System.out.print("f"); System.out.print("a"); System.out.print("l"); System.out.print("l"); System.out.print("i"); System.out.print("n"); System.out.print("l"); System.out.print("o"); System.out.print("v"); System.out.print("e"); System.out.print("\r\n"); flag = 1; notify(); } } } 更离谱的问题出现,打印结果: yingzi zhenming fallinlove 三个线程在执行一次之后就全部停了,或者说全部进入了等待状态,为什么会出现这一现象? 假设程序执行开始,先执行了线程2线程3,由于flag条件不满足,两个线程都进入等待状态,此时线程1开始执行,设置flag=2,执行完毕notify唤醒一个线程,并且由于自身条件也陷入等待状态,假设先唤醒的是线程3,由于flag值不对,线程3仍然处于等待状态,这种情况下,三个线程全部陷入等待状态 notify随机唤醒一个线程,是导致这一问题出现的原因,那么可否唤醒全部线程呢, notifyall就出现了,notifyAll可以唤醒全部线程,然后根据条件的判断执行相应的线程,但是这样做是由弊端的,那就是每次都要将所有线程唤醒,这是JDK1.5之前的处理办法,JDK1.5有新的方法解决这一问题 public class Thread1 { public static void main(String[] args) { final Printer printer = new Printer(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print1(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print2(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { printer.print3(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } } /** * 等待唤醒机制,两个线程间通信 * @author renzhenming * */ class Printer{ private int flag = 1; public void print1() throws InterruptedException{ synchronized(this){ while (flag !=1) { /** * obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞, * 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1), * 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁, * 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。 * (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后, * T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法) */ this.wait(); } System.out.print("y"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("z"); System.out.print("i"); System.out.print("\r\n"); flag = 2; /** * obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现), * 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁, * 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前, * 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。 */ this.notifyAll(); } } public void print2() throws InterruptedException{ synchronized (this) { while (flag != 2) { wait(); } System.out.print("z"); System.out.print("h"); System.out.print("e"); System.out.print("n"); System.out.print("m"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("\r\n"); flag = 3; notifyAll(); } } public void print3() throws InterruptedException{ synchronized (this) { while (flag != 3) { wait(); } System.out.print("f"); System.out.print("a"); System.out.print("l"); System.out.print("l"); System.out.print("i"); System.out.print("n"); System.out.print("l"); System.out.print("o"); System.out.print("v"); System.out.print("e"); System.out.print("\r\n"); flag = 1; notifyAll(); } } } 结果: yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove ... JDK1.5互斥锁 所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别: synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放. 而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能。 import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Thread1 { public static void main(String[] args) { final Printer printer = new Printer(); new Thread(new Runnable() { @Override public void run() { while (true) { try { printer.print1(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { printer.print2(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { printer.print3(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } } /** * 等待唤醒机制,互斥锁 * * @author renzhenming * */ class Printer { ReentrantLock r = new ReentrantLock(); Condition c1 = r.newCondition(); Condition c2 = r.newCondition(); Condition c3 = r.newCondition(); private int flag = 1; public void print1() throws InterruptedException { r.lock(); if (flag != 1) { c1.await(); } System.out.print("y"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("z"); System.out.print("i"); System.out.print("\r\n"); flag = 2; c2.signal(); r.unlock(); } public void print2() throws InterruptedException { r.lock(); if (flag != 2) { c2.await(); } System.out.print("z"); System.out.print("h"); System.out.print("e"); System.out.print("n"); System.out.print("m"); System.out.print("i"); System.out.print("n"); System.out.print("g"); System.out.print("\r\n"); flag = 3; c3.signal(); r.unlock(); } public void print3() throws InterruptedException { r.lock(); if (flag != 3) { c3.await(); } System.out.print("f"); System.out.print("a"); System.out.print("l"); System.out.print("l"); System.out.print("i"); System.out.print("n"); System.out.print("l"); System.out.print("o"); System.out.print("v"); System.out.print("e"); System.out.print("\r\n"); flag = 1; c1.signal(); r.unlock(); } } 结果,按照唤醒顺序执行线程,不再需要使用while判断,if即可 fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming fallinlove yingzi zhenming ...

优秀的个人博客,低调大师

java基础学习_面向对象(下)03_day10总结

==========================================================================================================================================================涉及的知识点有: 1:方法的形式参数和方法的返回值的问题(理解) (1)方法的形式参数为引用数据类型 (2)方法的返回值类型为引用数据类型 (3)链式编程 2:包(理解) (1)包其实就是文件夹。 (2)包的作用 (3)包的定义(掌握) (4)包的注意事项(掌握) (5)带包的编译和运行 (6)Java中不同包下的类与类之间的访问 3:导包(掌握) (1)导包的原因 (2)导包的格式 (3)package,import,class有没有顺序关系呢? 4:权限修饰符(掌握) (1)权限修饰符的权限测试 (2)这四种权限修饰符在任意时刻只能出现一种。 5:类及其组成所使用的常见修饰符(理解) (1)修饰符的分类 (2)常见的类及其组成所使用的修饰符 1.修饰类的 2.修饰成员变量的 3.修饰构造方法的 4.修饰成员方法的 (3)另外比较常见的修饰符组合规则 6:内部类(理解) (1)把类定义在另一个类的内部,该类就被称为内部类。 (2)内部类的访问特点 (3)内部类的分类(内部类的位置) (4)成员内部类 (5)成员内部类的面试题(填空) (6)局部内部类 (7)匿名内部类(很重要掌握) (8)匿名内部类在开发中的使用 (9)匿名内部类的面试题(补齐代码)==========================================================================================================================================================1:方法的形式参数和方法的返回值的问题(理解) (1)方法的形式参数为引用数据类型: 1.方法的形式参数为类名时:需要该类的对象。(匿名对象的时候已经讲过了) 2.方法的形式参数为抽象类名时:需要该抽象类的子类对象。 3.方法的形式参数为接口名时:需要该接口的实现类对象。 (2)方法的返回值类型为引用数据类型: 1.方法的返回值类型为类名时:返回的是该类的对象。 2.方法的返回值类型为抽象类名时:返回的是该类的子类对象。 3.方法的返回值类型为接口名时:返回的是该接口的实现类的对象。 (3)链式编程 对象.方法1().方法2().方法3(). ……… .方法n(); 这种用法: 其实 在方法1()调用完毕后,应该一个对象; 方法2()调用完毕后,应该返回一个对象。 方法3()调用完毕后,应该返回一个对象。 ………… 方法n()调用完毕后,返回的可能是对象,也可以不是对象。-----------------------------------------------------------------------------2:包(理解) (1)包其实就是文件夹。 (2)包的作用: A:区分同名的类(即把具有相同类名的类放到不同的包中)。 B:对类进行分类管理。 a:包按照功能分 cn.itcast.add 增加功能的包 AddStudent 增加学生的类 AddTeacher 增加老师的类 cn.itcast.delete 删除功能的包 DeleteStudent 删除学生的类 DeleteTeacher 删除老师的类 cn.itcast.update 修改功能的包 UpdateStudent 修改学生的类 UpdateTeacher 修改老师的类 cn.itcast.find 查找功能的包 FindStudent 查找学生的类 FindTeacher 查找老师的类 b:包按照模块分 cn.itcast.student 学生的包 AddStudent 增加学生的类 DeleteStudent 删除学生的类 UpdateStudent 修改学生的类 FindStudent 查找学生的类 cn.itcast.teacher 老师的包 AddTeacher 增加老师的类 DeleteTeacher 删除老师的类 UpdateTeacher 修改老师的类 FindTeacher 查找老师的类 小结:开发中,两种分类方式结合使用。--------------------------------------- (3)包的定义(掌握) 定义包的格式: package 包名; 多级包用.分开。 (4)包的注意事项(掌握): A:package语句必须是程序的第一条可执行的代码。(可执行=可被虚拟机识别) B:package语句在一个java文件中只能有一个。 C:如果没有package,默认表示无包名。--------------------------------------- (5)带包的编译和运行 A:手动式 a:编写一个带包的Xxx.java文件。 例如: package cn.itcast; class HelloWorld { public static void main(String[] args) { System.out.println("HelloWorld"); } } b:通过javac命令编译该Xxx.java文件,生成对应的Xxx.class文件。 c:手动创建包名。 d:把b步骤的Xxx.class文件放到c步骤的最底层包。 e:回到和包根目录在同一目录的地方,然后带包运行。 格式:java cn.itcast.Xxx--------------------------------------- B:自动式(掌握) a:编写一个带包的Xxx.java文件。 b:javac编译的时候带上-d即可 格式:javac -d . Xxx.java c:回到和包根目录在同一目录的地方,然后带包运行 格式:java cn.itcast.Xxx--------------------------------------- (6)Java中不同包下的类与类之间的访问 ----------------------------------------------------------------------------- 3:导包(掌握) (1)导包的原因:我们多次使用一个带包的类,都需要加包的全路径非常的麻烦,这个时候,Java就提供了导包的功能,提供了一个关键字import。 (2)导包的格式: java.lang不需要导入 import xxx.yyy.类名; 这种方式导入的是:导入到类的级别。 另一种: import xxx.yyy.*;(不建议) (3)package,import,class有没有顺序关系呢? 有。 package > import > class package:只能有一个 import:可以有多个 class:可以有多个,以后建议是一个。(因为每一个类是一个独立的单元)----------------------------------------------------------------------------- 4:权限修饰符(掌握) (1)权限修饰符的权限测试 小结:protected修饰的东西其实就是给子类用的。 所以以后在设计父类的时候,可以把private修饰的成员变量用protected修饰, 这样子类中就可以使用this关键字了,比如:this.name = name; 但是其实这样的做法不是很多,还是建议用private修饰最好。--------------------------------------- (2)这四种权限修饰符在任意时刻只能出现一种。 public class Demo {} -----------------------------------------------------------------------------5:类及其组成所使用的常见修饰符(理解) (1)修饰符的分类: 权限修饰符:private、默认、protected、public 状态修饰符:static、final 抽象修饰符:abstract (2)常见的类及其组成所使用的修饰符: 1.修饰类的: 有:默认、public、final、abstract 示例如下: class Demo {} //默认修饰符 public class Demo {} final class Demo {} //最终类,不能被继承。 abstract class Demo {} //抽象类 常用类的修饰符:public class Demo {} 特别注意:静态不可以修饰外部类,但是可以修饰内部类。--------------------------------------- 2.修饰成员变量的: 有:private、默认、protected、public、static、final 示例如下: private int x = 10; int y = 20; protected int z = 30; public int a = 40; public final int b = 50; //表示自定义的常量 public static int c = 60; //表示定义的通过类名访问的变量 public static final int d = 70; //表示自定义的通过类名访问的常量 常用成员变量的修饰符:private int x = 10;--------------------------------------- 3.修饰构造方法的: 有:private、默认、protected、public 示例如下: private Demo(){} Demo(String name){} protected Demo(String name, int age) {} public Demo(String name, int age, String address) {} 常用构造方法的修饰符:public Demo(String name, int age, String address) {} 小结:构造方法只能使用权限修饰符,即构造方法只提供访问权限,不提供其他的。 --------------------------------------- 4.修饰成员方法的: 有:private、默认、protected、public、static、final、abstract 示例如下: private void show() {} void show() {} protected void show() {} public void show() {} static void show() {} //静态方法,只能通过类名访问 abstract void show(); //抽象方法 final void show() {} //最终方法 常用成员方法的修饰符:public void show() {}--------------------------------------- (3)另外比较常见的修饰符组合规则: 示例如下: 修饰成员变量的: public static final int x = 10; //定义一个静态的最终变量(常量),只能通过类名访问 修饰成员方法的: public static void show() {} public abstract void show(); public final void show() {}-----------------------------------------------------------------------------6:内部类(理解) (1)把类定义在另一个类的内部,该类就被称为内部类。 举例:把类B定义在类A中,类B就被称为内部类。 (2)内部类的访问特点: A:内部类可以直接访问外部类的成员,包括私有成员。 B:外部类要想访问内部类成员,必须创建内部类的对象,通过内部类的对象去访问。 (3)内部类的分类(内部类的位置): A:成员位置:在类中的成员位置定义的类,被称为成员内部类。 B:局部位置:在类中的局部位置定义的类,被称为局部内部类。--------------------------------------- (4)成员内部类 成员内部类的常见修饰符及应用 A:private 为了保证数据的安全性 B:static 为了方便访问数据 注意:静态的内部类访问外部类的数据时,外部类的数据必须用静态修饰。 成员内部类不被静态修饰后的访问格式是: 外部类名.内部类名 对象名 = new 外部类名.new 内部类名(); 成员内部类被静态修饰后的访问格式是: 外部类名.内部类名 对象名 = new 外部类名.内部类名();--------------------------------------- (5)成员内部类的面试题(填空) 注意: 1:内部类和外部类没有继承关系。(这样你就不会使用super去访问外部类的成员变量了) 2:通过外部类名限定this对象。 Outer.this 1 class Outer { 2 public int num = 10; 3 4 class Inner { 5 public int num = 20; 6 7 public viod show() { 8 int num = 30; 9 10 System.out.println(num); //30 11 System.out.println(this.num); //20 12 System.out.println(Outer.this.num); //10 通过外部类名限定this对象。 13 System.out.println(new Outer().num);//10 通过创建外部类对象,来调用外部类的成员变量。 14 } 15 } 16 } 17 class InnerClassTest { 18 public static void main(String[] args) { 19 Outer.Inner oi = new Outer().new Inner(); 20 oi.show(); 21 } 22 } --------------------------------------- (6)局部内部类 A:局部内部类可以直接访问外部类的成员。 B:局部内部类在局部位置可以创建内部类对象,通过内部类对象调用内部类方法,来使用局部内部类功能。 C:局部内部类访问局部变量,局部变量必须加final修饰。为什么呢? 因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失, 局部对象还要使用这个局部变量。为了让数据还能继续被使用,就用fianl修饰局部变量, 这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。 通过反编译工具我们看到了,加入final后,堆内存直接存储的是值,而不是局部变量名。--------------------------------------- (7)匿名内部类(很重要掌握) A:匿名内部类是局部内部类的简化形式。 B:匿名内部类的前提: 存在一个类或者接口。 这里的类可以是具体类也可以是抽象类。 C:格式: new 类名或者接口名() { 重写方法; } D: 匿名内部类的本质是什么呢? 答:是一个继承了该类的子类或者实现了该接口的实现类的匿名对象。 简言之:匿名内部类是一个子类的匿名对象。 再简言之:匿名内部类是子类的对象。 小结:匿名内部类是一个结合体,是子类和对象的结合体。--------------------------------------- (8)匿名内部类在开发中的使用 我们在开发的时候,会看到抽象类,或者接口作为方法的形式参数。 而这个时候,我们知道实际需要的是一个子类的对象。 如果该方法仅仅调用一次,我们就可以使用匿名内部类的格式进行简化。 Android开发中这种格式见得多, JavaEE开发中见得少。 为什么呢? 因为匿名内部类的好处是: 匿名内部类用完之后就是垃圾(即只能用一次),就可以立即被垃圾回收器回收, 栈内存没有东西指向你,Android的手机内存小,要及时让这个对象被回收。 1 interface Person { 2 public abstract void study(); 3 } 4 5 class PersonDemo { 6 public void method(Person p) { 7 p.study(); 8 } 9 } 10 11 class PersonTest { 12 public static void main(String[] args) { 13 PersonDemo pd = new PersonDemo(); 14 pd.method(new Person() { 15 public void study() { 16 System.out.println("好好学习,天天向上"); 17 } 18 }); 19 } 20 } --------------------------------------- (9)匿名内部类的面试题(补齐代码) 1 interface Inter { 2 void show(); 3 } 4 5 class Outer { 6 //补齐代码 7 public static Inter method() { 8 return new Inter() { 9 public void show() { 10 System.out.println("HelloWorld"); 11 } 12 }; 13 } 14 } 15 16 class OuterDemo { 17 public static void main(String[] args) { 18 Outer.method().show(); //"HelloWorld" 19 } 20 /* 21 1:Outer.method()可以看出method()应该是Outer中的一个静态方法。 22 2:Outer.method().show()可以看出method()方法的返回值是一个对象。 23 又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。 24 */ 25 } =============================================================================我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

java基础学习_面向对象(下)01_day08总结

==========================================================================================================================================================涉及到的知识点有:1:继承(掌握) (0)Java继承概述 (1)继承定义 (2)Java中如何表示继承呢?格式是什么呢? (3)继承的好处 (4)继承的弊端 A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。 B:打破了封装性。 (5)Java中继承的特点 (6)继承的注意事项 A:Java中类只支持单继承,不支持多继承。 B:Java中可以多层(重)继承(继承体系) (7)那么什么时候使用继承呢? A:继承体现的是:is a的关系。 B:采用假设法。 (8)Java继承中的成员关系 A:成员变量 B:构造方法 C:成员方法 (9)方法重写:(方法=成员方法) (10)方法重写的两个面试题 A:Override和Overload的区别?Overload是否可以改变返回值类型? B:this和super的区别和各自的作用? (11)数据初始化的面试题 A:一个类的初始化过程 B:子父类的构造执行过程 C:子父类的初始化(分层初始化) (12)继承案例 A:学生和老师案例 B:猫狗案例的分析和实现==========================================================================================================================================================1:继承(掌握) (0)Java继承概述 (1)继承定义:把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系,这多个类就具备了这些内容。这个关系叫继承。 (2)Java中如何表示继承呢?格式是什么呢? A:用关键字extends表示。 B:格式: class 子类名 extends 父类名 {}--------------------------------------- (3)继承的好处: A:提高了代码的复用性。 B:提高了代码的维护性。 C:让类与类之间产生了一个关系,是多态的前提。 类与类产生了关系,其实也是继承的一个弊端: 类的耦合性增强了。--------------------------------------- (4)继承的弊端: A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。 开发设计的原则:低耦合,高内聚。 耦合:类与类之间的关系。 内聚:自己完成某件事情的能力。 B:打破了封装性。 因为我们曾经说过:一个类中的成员尽量不要让外界直接访问,子类继承父类后,那么父类的封装就被打破了。--------------------------------------- (5)Java中继承的特点: A:Java中类只支持单继承,不支持多继承。 即:一个类只能有一个父类,不可以有多个父类。 class Father {} class Mother {} class Son exnteds Father {} //正确的 class Son extends Father,Mother {} //错误的 有些语言是支持多继承的额,比如c++。其格式是:extends 类1,类2,... B:Java中可以多层(重)继承(继承体系) class A {} class B extends A {} class C extends B {}--------------------------------------- (6)继承的注意事项: A:子类不能继承父类的私有成员(成员变量和成员方法)。 其实这也体现了继承的另一个弊端:打破了封装性。 因为我们曾经说过:一个类中的成员尽量不要让外界直接访问,子类继承父类后,那么父类的封装就被打破了。 B:子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。 因为构造方法比较特殊,谈不上继承,构造方法是对对象进行数据初始化的。 C:不要为了部分功能而去继承。 例如: class A { public void show1(){} public void show2(){} } class B { public void show2(){} public void show3(){} } //我们发现B类中出现了和A类一样的show2()方法,所以,我们就用继承来体现。 class B extends A { public void show3(){} } 这样其实不好,因为这样你不但有了show2(),而且还多了show1()。 有可能show1()不是你想要的。--------------------------------------- (7)那么什么时候使用继承呢? A:继承体现的是:is a的关系。 例如: Person Student Teacher 水果 苹果 香蕉 橘子 B:采用假设法。 假设有两个类A,B。只有他们符合A是B的一种,或者B是A的一种的时候,就可以考虑使用继承。--------------------------------------- (8)Java继承中的成员关系: A:成员变量 a:子类的成员变量名称和父类中的成员变量名称不一样时,这个太简单。 b:子类的成员变量名称和父类中的成员变量名称一样时,这个怎么访问呢? 在子类的方法中访问一个成员变量的查找顺序: 1.在子类方法的局部范围找,有就使用。 2.在子类的成员范围找,有就使用。 3.在父类的成员范围找,有就使用。 4.都找不到,就报错。 B:构造方法 a:子类的所有的构造方法默认都会去访问父类的无参构造方法。 因为子类会继承父类中的数据,可能还会使用父类的数据。 所以,子类初始化之前,一定要先完成父类数据的初始化。 特别注意:其实子类的每一个构造方法的第一条语句默认都是:super(); b:父类中如果没有无参构造方法(也即父类中只给了带参构造方法),子类的构造方法怎么办? 法1:子类的构造方法通过 super(...); 去显示调用父类的带参构造方法。 法2:子类的构造方法通过 this();/this(...); 调用本类的其他的构造方法,但是子类的其他的构造方法中一定会有一个去访问了父类的带参构造方法。 法3:让父类提供无参构造。 注意事项: this();/this(...);/super(...); 这三个语句访问子类或父类的构造方法的时候,必须放在第一句语句上。 否则,就可能会对父类数据进行多次初始化。 C:成员方法 a:子类的成员方法名称和父类中的成员方法名称不一样时,这个太简单。 b:子类的成员方法名称和父类中的成员方法名称一样时,这个怎么访问呢? 通过子类对象访问一个成员方法的查找顺序: 1.在子类的成员方法中找,有就使用。 2.在父类的成员方法中找,有就使用。 3.都找不到,就报错。--------------------------------------- (9)方法重写:(方法=成员方法) 方法重写:子类中出现了和父类中方法声明一模一样的方法(方法名、参数列表和返回值类型都一样),也被称为方法覆盖、方法复写。 方法重载:本类中出现的方法名一样,参数列表不同,与返回值类型无关的方法。 方法重写的使用特点: 如果子类和父类的方法名不同,就调用各自对应的方法。 如果子类和父类的方法名相同,最终使用的是子类自己的方法。 方法重写的应用: 当子类需要父类的功能,而功能的主体子类还有自己特有的内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类自己特有的内容。 方法重写的注意事项: 1.父类中的私有方法不能被重写。 因为父类私有方法子类根本无法继承。 2.子类重写父类方法时,访问权限不能更低。(最好权限要一致。) 默认的权限要小于public的权限。 3.父类的静态方法,子类也必须通过静态方法进行重写。 (其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解。) 因为方法是跟对象相关的,而静态是跟类相关的。 小结:子类重写父类方法的时候,最好声明一模一样。--------------------------------------- (10)方法重写的两个面试题: A:Override和Overload的区别?Overload是否可以改变返回值类型? 方法重写: 在子类中,出现和父类中一模一样的方法声明的现象。(包含方法名、参数列表和返回值类型都一样) 方法重载: 同一个类中,出现的方法名相同,参数列表不同,与返回值类型无关的现象。 方法重载能改变返回值类型,因为它和返回值类型无关。 Override:方法重写 Overload:方法重载 B:this和super的区别和各自的作用? this: 代表当前类的对象引用。 super:代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员。) 应用场景: 成员变量: this.成员变量 super.成员变量 构造方法: this(...) super(...) 成员方法: this.成员方法 super.成员方法--------------------------------------- (11)数据初始化的面试题 A:一个类的初始化过程 先进行成员变量的初始化: 默认初始化 显示初始化 构造方法初始化 B:子父类的构造方法发的执行过程 子类的所有的构造方法默认都会去访问父类的无参构造方法。 C:子父类的初始化(分层初始化) 先进行父类初始化,然后进行子类初始化。--------------------------------------- (12)继承案例: A:学生和老师案例 继承前案例 继承后案例 B:猫狗案例的分析和实现 做设计的时候,先找到具体的事物,然后发现具体的事物的共性,再提取出一个父类。=============================================================================我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

java基础学习_面向对象(上)03_day08总结

==========================================================================================================================================================涉及到的知识点有: 0:前面学习通过方法对代码进行改进,为什么要给方法使用静态呢? 1:Java如何制作帮助文档(API)(了解) 2:通过JDK提供的帮助文档(API)学习了Math类(掌握) 3:Java中的代码块(理解)==========================================================================================================================================================0:前面学习通过方法对代码进行改进,为什么要给方法使用静态呢? 答:因为main方法是静态的,而静态方法只能访问静态的成员变量和静态的成员方法。 所以之前我们的方法是使用静态来修饰的,即静态的方法。 如果我们把static去掉呢?即该如何调用非静态的方法呢? 答:通过创建对象,使用对象进行调用啊! 测试类的作用是什么? 答:创建其他类的对象,调用其他类的功能。 在同一个文件夹下,Java的类定义在两个文件中和定义在一个文件中其实是一样的。--------------------------------------- 工具类中的方法使用静态修饰后,又让外界不能通过创建对象方式去访问该方法,只能通过工具类名来访问该方法,该如何办呢? 答:把工具类中的构造方法私有即可。 如下图所示01: -----------------------------------------------------------------------------1:Java如何制作帮助文档(API)(了解) 步骤如下: (1)写一个工具类 (2)对这个类加入文档注释 (3)用工具解析文档注释 javadoc工具 (4)格式 javadoc -d 目录 -author -version ArrayTool.java 制作帮助文档(API)出错问题解决: 找不到可以文档化的公共或受保护的类 这句话告诉我们对想要操作的类的权限不够。在类前面加上public即可。 如下图所示02: --------------------------------------- 将来做开发的时候,我们给别人的东西就是Xxx.class文件和帮助文档(API)。别人通过帮助文档(API)来使用Xxx.class文件。 这就是面向对象思想,我才不管你是怎们实现的呢!我满足你的条件会用就可以。-----------------------------------------------------------------------------2:通过JDK提供的帮助文档(API)学习了Math类(掌握) (1)API(Application Programming Interface) 应用程序编程接口(帮助文档) (2)如何使用帮助文档(API)呢? 1:打开帮助文档 2:点击显示,找到索引,看到输入框 3:知道你要找谁?以Scanner举例 4:在输入框里面输入Scanner,然后回车 5:第一步:看包 java.lang包下的类不需要导入,其他的全部需要导入。 要导入: java.util.Scanner 6:再简单的看看该类的解释说明和例子,别忘了看看该类的版本。 7:看类的结构: 成员变量 字段摘要 构造方法 构造方法摘要 成员方法 方法摘要 8:学习构造方法的两种情况: A:有构造方法 就创建该类的对象,通过对象调用。 B:没有构造方法 该类的成员变量和成员方法可能都是静态的,通过类名调用。 9:看成员方法: A:看左边 看是否是静态的成员方法:如果是静态,可以通过类名调用。 看返回值类型:人家返回什么,你就用什么接收。 B:看右边 看方法名:方法名称不要写错了。 看参数列表:人家要什么参数,你就给什么参数;人家要几个参数,你就给几个参数。 (3)API中的Math类的示范使用 A:是针对数学执行基本运算进行操作的类。 B:没有构造方法,因为它的成员都是静态的。 C:产生随机数: public static double random() 返回值为double的正值,大于等于0.0 ,小于1.0。即:[0.0, 1.0) 返回的值是从该范围(大约)均匀分布的伪随机选择的。 D:如何产生一个1~100([1, 100])之间的随机数? int number = (int)(Math.random() * 100) + 1; E:猜数字小游戏。-----------------------------------------------------------------------------3:Java中的代码块(理解) (1)在Java中用{}括起来的代码。 (2)代码块分类:(根据其位置和声明的不同) A:局部代码块 在方法定义中,用于限定变量的生命周期,及早释放,提高内存利用率。 B:构造代码块 在类中方法外出现(即在类中的成员位置),可以把多个构造方法方法中相同的代码存放到一起,用于对对象进行初始化,每次调用构造方法都执行,并且在构造方法前执行。 C:静态代码块 在类中方法外出现(即在类中的成员位置),并加上static修饰,用于对类进行初始化,静态在类加载的时候就执行了,并且只执行一次。 D:同步代码块 多线程部分讲解。 (3)面试题: 静态代码块、构造代码块、构造方法的执行顺序问题? 静态代码块 > 构造代码块 > 构造方法 只执行一次 每次调用构造方法都执行 (4)代码块练习题: 看代码写程序的执行结果。============================================================================= 我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

基础篇视频教程)

只需要花10几分钟,跟着教程完整做一遍,你就能自己用Python做出词云了。 《如何用Python做词云?》图文版发布于2017年6月,是我数据科学系列教程中的第一篇。 目前仅简书一个平台,阅读数量就已经超过2万。 我一直不断收到读者的留言和来信,询问自己动手尝试过程中遇到的问题。 大部分的疑问,来自于Windows平台用户。 有时候,因为一个软件包选择错误,就会遇到各种报错。 错误也许是因为新版本的推出,也许是因为32位和64位平台没有正确区分……初学者如果得不到帮助,很容易迅速丧失完成的信心和兴趣。 为了让大家花费更少的时间试错,更高效地掌握词云制作基本方法,我制作了对应的视频教程。 视频以Windows平台上,基于Python 3.6的32位版本Anaconda为工作环境录制。 注意你需要先安装好Anaconda环境。安装的方法请参考我的另一份视频教程《如何安装Python运行环境Anaconda?(视频教程)》。 我把数据和附加软件包的安装文件都打包提供了给你。可以访问这个链接来下载。 只需要花10几分钟,跟着教程完整做一遍,你就能自己用Python做出词云了。 视频链接在这里。 注意视频播放默认选择“高清”,但其实是支持1080P的。你可以在各种不同的屏幕上以最高分辨率清晰播放,以看清细节。 完整观看了视频以后,你就可以做出基本款的英文词云了。你可能还会有以下疑问: 如何对中文文本做词云呢? 如何做出指定边框形状的词云呢? 这些内容,欢迎你参考我的“玉树芝兰”数据科学系列图文教程来学习。 我也会在后续的视频教程中,讲述和展示相关的内容。 喜欢请点赞。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)。 如果你对数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

优秀的个人博客,低调大师

java基础学习_面向对象(上)02_day07总结

========================================================================================================================================================== 涉及到的知识点有: 1:成员变量和局部变量的区别(理解) 2:类作为形式参数的问题?(理解) 3:匿名对象(理解) 4:封装(理解) 5:private关键字(掌握) 6:this关键字(掌握) 7:构造方法(掌握) (1)作用 (2)格式 (3)构造方法的注意事项 (4)给成员变量赋值的方式有两种 (5)输出成员变量值的方式有两种 (6)标准学生案例 8:类的初始化过程代码:Student s = new Student();做了哪些事情?(理解) 9:面向对象的练习题(掌握) (1)标准的手机类的定义和手机测试类。 (2)Demo类中有求和方法,Test类进行测试。 (3)长方形案例。 (4)员工案例。 (5)MyMath案例(自己提供加减乘除并测试)。 10:static关键字(理解) (0) 针对多个对象有共同的成员变量的时候 (1)静态的意思,可以修饰成员变量和成员方法 (2)静态的特点 (3)静态的内存图 (4)静态的注意事项 (5)静态变量和成员变量的区别 (6)main方法是静态的解释 ==========================================================================================================================================================1:成员变量和局部变量的区别(理解) (1)在类中的位置不同: 成员变量:在类中方法外。 局部变量:在方法定义中或者方法声明上(即形参)。 (2)在内存中的位置不同: 成员变量:在堆中。 局部变量:在栈中。 (3)生命周期不同: 成员变量:随着对象的创建而存在,随着对象的消失而消失。 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失。 (4)初始化值不同: 成员变量:有默认值。 局部变量:没有默认值,必须定义,赋值,然后才能使用。 (5)注意事项: 1.局部变量名称可以和成员变量名称一样,但是在方法中使用该变量的时候,采用的是就近原则(即先找小范围,再找大范围)。 2.局部变量前不能放置任何访问修饰符 (private,public和protected)。final可以用来修饰局部变量。被final修饰的局部变量变为常量了。 3.修饰成员变量的修饰符有:private、默认、protected、public、static、final。 ----------------------------------------------------------------------------- 2:类作为形式参数的问题?(理解) (0)之前学的形式参数问题: 基本数据类型(数值型、字符型、布尔型):形式参数的改变不影响实际参数。 引用数据类型(类、接口、数组):形式参数的改变会直接影响实际参数。 (1)如果一个方法的形参的数据类型是一个类时(即引用数据类型),这里的形参其实需要的是该类的一个具体的对象。 如下图所示01: -----------------------------------------------------------------------------3:匿名对象(理解) (1)就是没有名字的对象(是对象的一种简化表示形式)。 (2)匿名对象的应用场景: A:对象调用方法仅仅一次的时候。 注意:调用多次对象的时候,不适合。 那么,这种匿名对象调用方法有什么好处吗? 有,匿名对象调用方法完毕后就是垃圾。可以被垃圾回收器回收,提高内存使用效率。 B:匿名对象可以作为实际参数进行传递。 如下图所示02: ----------------------------------------------------------------------------- 4:封装(理解) (1)是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 (2)好处: A:隐藏实现细节,提供公共的访问方式。 B:提高了代码的复用性。 C:提高了代码的安全性。 (3)设计原则 把不想让外界知道的实现细节给隐藏起来,把属性隐藏,提供公共的访问方式。 (4)private是封装的一种体现之一。 封装体有这几个: (1)类 (2)方法 (3)private修饰成员变量-----------------------------------------------------------------------------5:private关键字(掌握) (0)是一个权限修饰符。 (1)私有的意义,可以修饰成员变量和成员方法。(一般来说,private多用来修饰成员变量,少用来修饰成员方法。) (2)特点: 被private修饰的成员只能在本类中被访问。要想访问它,它就要对外提供公共的访问方式。 (3)private的应用: 以后再写一个类的时候: 把所有的成员变量给private了。 提供对应的getXxx()/setXxx()方法。-----------------------------------------------------------------------------6:this关键字(掌握) (0)name = name; //把自己赋值给自己,没有任何意义。(变量的使用规则:就近原则) (1)代表当前类的引用对象。简单的记,它就代表当前类的一个具体对象。 记住:哪个对象调用方法,在该方法内部的this就代表哪个对象。 (2)this的应用场景: A:解决了局部变量隐藏成员变量的问题。 B:其实this还有其他的应用,其他用法后面和super一起讲解。 如下图所示03/04: -----------------------------------------------------------------------------7:构造方法(掌握) (1)作用:用于给创建的对象的数据进行初始化。 (2)格式: A:方法名和类名相同。 B:没有返回值类型,连void都不能有。 C:没有具体的返回值。 思考题:构造方法中可不可以有return语句呢? 可以。而是我们写成这个样子就OK了:return; 其实,在任何的void类型的成员方法的最后你都可以写上:return; 表示结束该方法。 (3)构造方法的注意事项: A:如果我们没有给出构造方法,系统将自动提供一个无参构造方法,且是不带修饰符的,即默认修饰符的。 B:如果我们给出了构造方法,不管我们给出的是无参还是有参构造函数,系统将不再提供默认的无参构造方法。 注意:当我们给出了有参构造函数时,但如果我们还想使用无参构造方法,此时就必须自己给出无参构造函数。建议永远自己给出无参构造方法。 C:构造方法也是方法,也可以方法重载的。 (4)给成员变量赋值的方式有两种:(只写一种即可) 方式1:setXxx(参数类型 成员变量名)的成员方法(实际开发时用方式1,该方式更灵活。) 方式2:带一个或者多个参数的构造方法(讲课时用方式2) (5)输出成员变量值的方式有两种:(只写一种即可) 方式1:getXxx() {...}(实际开发时用方式1,该方式更灵活。) 方式2:通过调用show()方法实现(其实方式2一般没有,感觉调用getXxx()输出学生信息比较麻烦,加入一个方法show(),输出学生对象的成员变量信息。) (6)标准学生案例: 类的组成有:成员变量、构造方法、成员方法。 构造方法:没有返回值。 但是也可以在最后写一个return; 其实在任何返回值为void类型的成员方法的最后都可以写上:return; 表示结束该方法。 成员方法的分类: 根据返回值类型可以分为: 1.void类型的成员方法。 2.非void类型的成员方法。 根据形式参数可以分为: 1.无参成员方法 2.有参成员方法--------------------------------------- 1 //学生类 2 class Student { 3 private String name; 4 private int age; 5 6 //无参构造方法 7 public Student() { 8 } 9 10 //有多个参数的构造方法 11 public Student(String name,int age) { 12 this.name = name; 13 this.age = age; 14 } 15 16 //有明确返回值的、无参的成员方法(单独获取数据)。 17 public String getName() { 18 return name; 19 } 20 //返回void类型的、有参的成员方法。 21 public void setName(String name) { 22 this.name = name; 23 } 24 25 //有明确返回值的、无参的成员方法(单独获取数据)。 26 public int getAge() { 27 return age; 28 } 29 30 //返回void类型的、有参的成员方法。 31 public void setAge(int age) { 32 this.age = age; 33 } 34 35 //输出所有成员变量的值(集体获取数据) 36 public void show() { 37 system.out.println(name+"---"+age); 38 } 39 } 40 41 //学生测试类 42 class StudentDemo { 43 public static void main(String[] args) { 44 //方式1 45 Student s1 = new Student(); 46 s1.setName("林青霞"); 47 s1.setAge(27); 48 System.out.println(s1.getName()+"---"+s1.getAge()); 49 s1.show(); 50 51 //方式2 52 Student s2 = new Student("刘意",30); 53 System.out.println(s2.getName()+"---"+s2.getAge()); 54 s2.show(); 55 } 56 } ----------------------------------------------------------------------------- 8:类的初始化过程代码:Student s = new Student();做了哪些事情?(理解) (1)把Student.class文件加载到内存,在Student.class文件中找到main方法并放到栈。 (2)在栈内存为s开辟空间(Student s =)。 (3)在堆内存为学生对象申请空间(new Student();)。 (4)给学生的成员变量进行默认初始化。namne = null; age = 0; (5)给学生的成员变量进行显示初始化。name = 林青霞; age = 27; (6)通过构造方法给成员变量进行初始化。name = 刘意; age =30; (7)学生对象初始化完毕后,,把堆内存的地址赋值给栈内存的s变量。--------------------------------------- 1 class Student { 2 private String name = "林青霞"; 3 private int age = 27; 4 5 public Student() { 6 name = "刘意"; 7 age = 30; 8 } 9 } 10 11 class StudentDemo { 12 public static void main(String[] args) { 13 Student s = new Student(); 14 } 15 } 如下图所示05: ----------------------------------------------------------------------------- 9:面向对象的练习题(掌握) (1)标准的手机类的定义和手机测试类。 (2)Demo类中有求和方法,Test类进行测试。 什么时候定义成员变量? 答:当该变量是用来描述一个类的时候。---------------------------------------示例代码如下: 1 /* 2 定义一个类Demo,其中定义一个求两个数据和的方法, 3 定义一个测试类Test,进行测试。 4 5 变量什么时候定义为成员变量? 6 答:如果这个变量是用来描述这个类的信息的,那么,该变量就应该定义为成员变量。 7 8 变量到底定义在哪里好呢? 9 答:变量的范围是越小越好。因为能及时的被回收。 10 */ 11 //方式1 12 /* 13 class Demo { 14 public int sum() { 15 int a = 10; 16 int b = 20; 17 int c = a + b; 18 return c; 19 } 20 } 21 */ 22 //方式1满足了我们的要求,但是不好。 23 //因为参与操作的数据现在是固定的。 24 25 //方式2 26 /* 27 class Demo { 28 public int sum(int a,int b) { 29 return a + b; 30 } 31 } 32 */ 33 //方式2可以满足我们的要求,但是呢我们学习过来面向对象的思想。 34 //我就再想,a,b可不可以定义为成员变量呢? 35 //如果可以,我们再改进一版如下: 36 37 //方式3 38 class Demo { 39 int a; 40 int b; 41 42 public int sum() { 43 return a + b; 44 } 45 } 46 //虽然这种方式可以,并且好像是符合了面向对象的思想。 47 //但是不好。 48 //因为我们曾经说过:类是一组相关的属性和行为的集合。 49 //并且类是由事物转换过来的,而类中的成员变量就是事物的属性,属性是用来描述事物的。 50 //同理:成员变量其实是用来描述类的。 51 52 //测试类 53 class Test { 54 public static void main(String[] args) { 55 //创建对象 56 //方式1测试 57 /* 58 Demo d = new Demo(); 59 System.out.println(d.sum()); 60 */ 61 62 //方式2测试 63 /* 64 Demo d = new Demo(); 65 int a = 10; 66 int b = 20; 67 System.out.println(d.sum(a,b)); 68 */ 69 70 //方式3测试 71 Demo d = new Demo(); 72 d.a = 10; 73 d.b = 20; 74 System.out.println(d.sum()); 75 } 76 } --------------------------------------- (3)长方形案例。---------------------------------------示例代码如下: 1 /* 2 定义一个长方形类,定义求周长和面积的方法, 3 然后定义一个测试类Test2,进行测试。 4 5 长方形的类: 6 有成员变量: 7 长,宽 8 有成员方法: 9 求周长:(长+宽)*2; 10 求面积:长*宽 11 12 注意: 13 import必须出现在所有的class前面。 14 */ 15 16 import java.util.Scanner; 17 18 class ChangFangXing { 19 //长方形的长 20 private int length; 21 //长方形的宽 22 private int width; 23 24 //无参构造方法 25 public ChangFangXing(){} 26 27 //仅仅提供setXxx()即可 28 public void setLength(int length) { 29 this.length = length; 30 } 31 32 public void setWidth(int width) { 33 this.width = width; 34 } 35 36 //求周长 37 public int getZhouChang() { 38 return (length + width) * 2; 39 } 40 41 //求面积 42 public int getArea() { 43 return length * width; 44 } 45 } 46 47 class Test2 { 48 public static void main(String[] args) { 49 //创建键盘录入对象 50 Scanner sc = new Scanner(System.in); 51 52 System.out.println("请输入长方形的长:"); 53 int length = sc.nextInt(); 54 System.out.println("请输入长方形的宽:"); 55 int width = sc.nextInt(); 56 57 //创建长方形对象 58 ChangFangXing cfx = new ChangFangXing(); 59 //先给成员变量赋值 60 cfx.setLength(length); 61 cfx.setWidth(width); 62 63 System.out.println("周长是:"+cfx.getZhouChang()); 64 System.out.println("面积是:"+cfx.getArea()); 65 } 66 } --------------------------------------- (4)员工案例。 定义一个员工类,自己分析出几个成员变量来, 然后给出成员变量,构造方法,getXxx()/setXxx()方法, 以及一个显示所有成员信息的show()方法。并测试。 (5)MyMath案例(自己提供加减乘除并测试)。 没有成员变量和构造方法,只有成员方法。----------------------------------------------------------------------------- 10:static关键字(理解) (0) 针对多个对象有共同的成员变量的时候, Java就提供了一个关键字来修饰:static。 (1)静态的意思,可以修饰成员变量和成员方法。 (2)静态的特点: A:随着类的加载而加载。(回想main方法) B:优先于对象存在。 C:被类的所有对象共享。 这其实也是我们判断该不该使用静态的依据。 举例:饮水机和水杯的问题思考。(成员变量饮水机就用静态来修饰) D:可以通过类名调用。 既可以通过对象名调用,也可以通过类名调用。 建议通过类名调用。 (因为静态修饰的内容一般我们称之为:类成员,与类相关的。) (不是静态修饰的内容一般我们称之为:对象成员,与对象相关的。)示例代码如下: 1 class Student { 2 //非静态的成员变量 3 int num = 10; 4 5 //静态的成员变量 6 static int num2 = 20; 7 } 8 9 class StudentDemo { 10 public static void main(String[] args) { 11 Student s = new Student(); 12 System.out.println(s.num); 13 14 //通过类名调用 15 System.out.println(Student.num2); 16 //通过对象名调用 17 System.out.println(s.num2); 18 } 19 } ----------------------------------------------------------------------------- (3)静态的内存图 静态的内容在方法区的静态区。 如下图所示06: ----------------------------------------------------------------------------- (4)静态的注意事项: A:在静态方法中没有this对象。 如何理解呢? 答:因为静态是随着类的加载而加载,而this随着对象的创建而存在。 即静态比对象先存在。 B:静态方法只能访问静态的成员变量和静态的成员方法。 如下图所示07: ----------------------------------------------------------------------------- (5)静态变量和成员变量的区别: A:所属不同 静态变量:属于类,也称为类变量。 成员变量:属于对象,也称为对象变量或实例变量。 B:在内存中的位置不同 静态变量:存储于方法区的静态区。 成员变量:存储于堆内存。 C:生命周期不同 静态变量:静态变量是随着类的加载而加载,随着类的消失而消失。 成员变量:成员变量是随着对象的创建而存在,随着对象的消失而消失。 D:调用不同 静态变量:可以通过对象名调用,也可以通过类名调用。 成员变量:只能通过对象名调用。----------------------------------------------------------------------------- (6)main方法是静态的解释 public static void main(String[] args) {...} public:公共的,访问权限是最大,由于main方法是被jvm调用的,所以权限要够大。 static:静态的,不需要创建对象。通过类名就可以调用,方便jvm的调用。 void:方法的返回值是返回给调用者的,而main方法是被jvm调用的,返回值给jvm没有意义。 main:就是一个常见的名称,是一个常见的程序入口。 String[] args:是一个字符串数组,早期用于接收键盘录入数据,提供程序的灵活性。 格式:java MainDemo hello world java 如下图所示08: =============================================================================我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

java基础学习_面向对象(上)01_day07总结

==========================================================================================================================================================涉及到的知识点有:1:面向对象(掌握) (0)面向过程和面向过程开发 (1)面向对象思想概述 (2)面向对象的思想特点 (3)把大象装进冰箱(理解) (4)类与对象的关系 (5)类的定义及使用 (6)案例 (7)内存图 (8)Java程序的开发、设计和特征==========================================================================================================================================================1:面向对象(掌握) (0)面向过程和面向过程开发 面向过程:每一个具体步骤中我们都是参与者(执行者),并且需要面对具体的每一个步骤和过程。 面向过程开发:其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。 (1)面向对象思想概述 面向对象是基于面向过程的编程思想。 (2)面向对象的思想特点: A:是一种更符合我们思考习惯的思想。 B:把复杂的事情简单化。 C:让我们从执行者(参与者)变成了指挥者。(角色发生了转换) 举例: 买电脑: 面向过程:我先了解电脑-->了解我自己的需求:学习用-->找对应的参数信息-->去中关村买电脑-->讨价还价-->买回电脑。 面向对象:我知道我要买电脑-->班长去给我买-->班长就买回来了。 洗衣服: 面向过程:把衣服脱下-->找一个盆-->放点洗衣粉-->加点水-->把衣服扔进去-->搓一搓-->清洗衣服-->拧干-->晾起来。 面向对象:把衣服脱下-->打开全自动洗衣机-->扔进去-->一键即可-->晾起来。 吃饭: 面向过程:去超市买菜-->摘菜-->洗菜-->切菜-->炒菜-->盛起来-->吃。 面向对象:去饭店-->服务员(点菜)-->厨师(做菜)-->服务员(端菜)--吃。 万事万物皆对象! 家常事物、买洗衣机和去饭店太不划算了,所以,找个对象。 (3)把大象装进冰箱(理解) A:面向过程实现 B:面向对象实现 注意:如何让我们的操作更符合面向对象思想呢? A:有哪些类呢? B:每个类有哪些东西呢? C:类与类之间的关系是什么呢? (4)类与对象的关系: 我们学习编程语言,是为了模拟现实世界的事物的。 A:现实世界的事物,我们如何表示呢? 属性 该事物的基本描述信息 行为 该事物的功能(该事物能够做什么) B:Java语言中最基本的单位是类。所以,我们就应该把事物用一个类来体现 C:类 成员变量 ==> 事物属性 成员方法 ==> 事物行为 D: 类:是一组相关的属性和行为的集合。是一个抽象的概念。 对象:是该类事物的具体体现(存在),是一个具体的实例。(对象) 类:可以理解为构造对象的一个蓝图或者模版,是抽象的概念。 对象:是以类为模型创建的具体实例,是对类的一种具体化。 举例: 类 --> 学生(抽象的) 对象 --> 班长就是一个对象(具体的) 如下图所示01: (5)类的定义及使用: A:类的定义:定义一个类其实就是定义该类的成员(成员变量和成员方法) 成员变量 定义格式和以前一样,就是位置不同,在类中,方法外。 成员方法 定义格式和以前一样,就是去掉了static。 B:如何使用类的内容? 在一个java文件中写两个类:一个基本的类,一个测试类。 注意:文件的名称和测试类的名称一致。 a:创建对象使用。格式如下: 类名 对象名 = new 类名(); b:如何使用成员变量和成员方法呢? 对象名.成员变量 对象名.成员方法() (6)案例: A:学生类的定义和使用 B:手机类的定义和使用 (7)内存图: A:一个对象的内存图 B:二个对象的内存图 C:三个对象的内存图 如下图所示02: (8)Java程序的开发、设计和特征 A:开发:就是不断的创建对象,通过对象调用功能。 B:设计:就是管理和维护对象间的关系。 C:特征 a:封装 b:继承 c:多态=============================================================================我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册